summaryrefslogtreecommitdiff
path: root/.config/qutebrowser
diff options
context:
space:
mode:
Diffstat (limited to '.config/qutebrowser')
-rw-r--r--.config/qutebrowser/Archive/alias.txt2
-rw-r--r--.config/qutebrowser/Archive/search.txt2
-rw-r--r--.config/qutebrowser/autoconfig.yml12
-rwxr-xr-x.config/qutebrowser/bookmarks/urls272
-rw-r--r--.config/qutebrowser/config.py1802
-rw-r--r--.config/qutebrowser/jupyter-profile/autoconfig.yml7
-rw-r--r--.config/qutebrowser/jupyter-profile/config.py1676
-rw-r--r--.config/qutebrowser/jupyter-profile/config/bookmarks/urls0
-rw-r--r--.config/qutebrowser/jupyter-profile/config/quickmarks0
-rwxr-xr-x.config/qutebrowser/jupyter-profile/keys.conf709
-rw-r--r--.config/qutebrowser/jupyter-profile/nord-qutebrowser.py336
-rwxr-xr-x.config/qutebrowser/jupyter-profile/qutebrowser.conf1612
-rwxr-xr-x.config/qutebrowser/keys.conf709
-rw-r--r--.config/qutebrowser/misc/Makefile33
-rw-r--r--.config/qutebrowser/misc/apparmor/usr.bin.qutebrowser41
-rw-r--r--.config/qutebrowser/misc/cheatsheet.svg3726
-rw-r--r--.config/qutebrowser/misc/qutebrowser.appdata.xml48
-rw-r--r--.config/qutebrowser/misc/qutebrowser.desktop12
-rw-r--r--.config/qutebrowser/misc/qutebrowser.nsi80
-rw-r--r--.config/qutebrowser/misc/qutebrowser.rcc13
-rw-r--r--.config/qutebrowser/misc/qutebrowser.spec76
-rw-r--r--.config/qutebrowser/misc/requirements/README.md20
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-check-manifest.txt3
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-check-manifest.txt-raw1
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-codecov.txt9
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-codecov.txt-raw1
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-flake8.txt27
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-flake8.txt-raw23
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pip.txt8
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt7
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt-raw1
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pylint-master.txt19
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pylint-master.txt-raw11
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pylint.txt19
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pylint.txt-raw7
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pyqt.txt4
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pyqt.txt-raw1
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pyroma.txt4
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-pyroma.txt-raw1
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-qutebrowser.txt-raw7
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-tests-git.txt38
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-tests.txt42
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-tests.txt-raw21
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-tox.txt9
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-tox.txt-raw1
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-vulture.txt3
-rw-r--r--.config/qutebrowser/misc/requirements/requirements-vulture.txt-raw1
-rwxr-xr-x.config/qutebrowser/misc/userscripts/cast156
-rwxr-xr-x.config/qutebrowser/misc/userscripts/dmenu_qutebrowser48
-rwxr-xr-x.config/qutebrowser/misc/userscripts/format_json42
-rwxr-xr-x.config/qutebrowser/misc/userscripts/getbib69
-rwxr-xr-x.config/qutebrowser/misc/userscripts/open_download115
-rwxr-xr-x.config/qutebrowser/misc/userscripts/openfeeds40
-rwxr-xr-x.config/qutebrowser/misc/userscripts/password_fill381
-rwxr-xr-x.config/qutebrowser/misc/userscripts/qute-keepass261
-rwxr-xr-x.config/qutebrowser/misc/userscripts/qute-lastpass172
-rwxr-xr-x.config/qutebrowser/misc/userscripts/qute-pass207
-rwxr-xr-x.config/qutebrowser/misc/userscripts/qutedmenu54
-rwxr-xr-x.config/qutebrowser/misc/userscripts/readability41
-rwxr-xr-x.config/qutebrowser/misc/userscripts/ripbang34
-rwxr-xr-x.config/qutebrowser/misc/userscripts/rss122
-rwxr-xr-x.config/qutebrowser/misc/userscripts/taskadd34
-rwxr-xr-x.config/qutebrowser/misc/userscripts/tor_identity52
-rwxr-xr-x.config/qutebrowser/misc/userscripts/view_in_mpv143
-rw-r--r--.config/qutebrowser/nord-qutebrowser.py336
-rw-r--r--.config/qutebrowser/qsettings/QtProject.conf8
-rwxr-xr-x.config/qutebrowser/quickmarks1
-rwxr-xr-x.config/qutebrowser/qutebrowser.conf1612
-rwxr-xr-x.config/qutebrowser/scripts/__init__.py3
-rwxr-xr-x.config/qutebrowser/scripts/asciidoc2html.py303
-rw-r--r--.config/qutebrowser/scripts/cycle-inputs.js46
-rw-r--r--.config/qutebrowser/scripts/dev/Makefile-dmg71
-rw-r--r--.config/qutebrowser/scripts/dev/__init__.py3
-rwxr-xr-x.config/qutebrowser/scripts/dev/build_release.py419
-rw-r--r--.config/qutebrowser/scripts/dev/check_coverage.py348
-rwxr-xr-x.config/qutebrowser/scripts/dev/check_doc_changes.py48
-rw-r--r--.config/qutebrowser/scripts/dev/ci/travis_backtrace.sh18
-rw-r--r--.config/qutebrowser/scripts/dev/ci/travis_install.sh108
-rw-r--r--.config/qutebrowser/scripts/dev/ci/travis_run.sh32
-rwxr-xr-x.config/qutebrowser/scripts/dev/cleanup.py69
-rw-r--r--.config/qutebrowser/scripts/dev/download_release.sh34
-rw-r--r--.config/qutebrowser/scripts/dev/gen_resources.py26
-rw-r--r--.config/qutebrowser/scripts/dev/gen_versioninfo.py85
-rw-r--r--.config/qutebrowser/scripts/dev/get_coredumpctl_traces.py176
-rw-r--r--.config/qutebrowser/scripts/dev/misc_checks.py163
-rw-r--r--.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/__init__.py1
-rw-r--r--.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/config.py84
-rw-r--r--.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/modeline.py63
-rw-r--r--.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/openencoding.py83
-rw-r--r--.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/settrace.py49
-rw-r--r--.config/qutebrowser/scripts/dev/pylint_checkers/setup.py25
-rwxr-xr-x.config/qutebrowser/scripts/dev/quit_segfault_test.sh14
-rw-r--r--.config/qutebrowser/scripts/dev/recompile_requirements.py136
-rwxr-xr-x.config/qutebrowser/scripts/dev/run_profile.py93
-rw-r--r--.config/qutebrowser/scripts/dev/run_pylint_on_tests.py79
-rwxr-xr-x.config/qutebrowser/scripts/dev/run_vulture.py196
-rwxr-xr-x.config/qutebrowser/scripts/dev/segfault_test.py120
-rwxr-xr-x.config/qutebrowser/scripts/dev/src2asciidoc.py561
-rw-r--r--.config/qutebrowser/scripts/dev/standardpaths_tester.py69
-rw-r--r--.config/qutebrowser/scripts/dev/strip_whitespace.sh10
-rwxr-xr-x.config/qutebrowser/scripts/dev/ua_fetch.py122
-rwxr-xr-x.config/qutebrowser/scripts/dev/update_3rdparty.py166
-rwxr-xr-x.config/qutebrowser/scripts/dictcli.py283
-rwxr-xr-x.config/qutebrowser/scripts/hist_importer.py174
-rwxr-xr-x.config/qutebrowser/scripts/hostblock_blame.py55
-rwxr-xr-x.config/qutebrowser/scripts/importer.py349
-rwxr-xr-x.config/qutebrowser/scripts/keytester.py56
-rwxr-xr-x.config/qutebrowser/scripts/link_pyqt.py233
-rwxr-xr-x.config/qutebrowser/scripts/open_url_in_instance.sh15
-rwxr-xr-x.config/qutebrowser/scripts/setupcommon.py74
-rw-r--r--.config/qutebrowser/scripts/testbrowser/cpp/webengine/main.cpp13
-rw-r--r--.config/qutebrowser/scripts/testbrowser/cpp/webengine/testbrowser.pro6
-rw-r--r--.config/qutebrowser/scripts/testbrowser/cpp/webkit/main.cpp13
-rw-r--r--.config/qutebrowser/scripts/testbrowser/cpp/webkit/testbrowser.pro6
-rwxr-xr-x.config/qutebrowser/scripts/testbrowser/testbrowser_webengine.py50
-rwxr-xr-x.config/qutebrowser/scripts/testbrowser/testbrowser_webkit.py56
-rwxr-xr-x.config/qutebrowser/scripts/utils.py103
117 files changed, 20693 insertions, 0 deletions
diff --git a/.config/qutebrowser/Archive/alias.txt b/.config/qutebrowser/Archive/alias.txt
new file mode 100644
index 0000000..a768263
--- /dev/null
+++ b/.config/qutebrowser/Archive/alias.txt
@@ -0,0 +1,2 @@
+ c.aliases = { 'w': 'session-save', 'q': 'quit', 'wq': 'quit --save', 'qtb': 'open https://www.qutebrowser.org/', 'mbt': 'open t https://signin1.bt.com/login/emailloginform', 'Ombt': 'open -t https://signin1.bt.com/login/emailloginform', 'nx': 'open http://next-episode.net/', 'Onx': 'open -t http://next-episode.net/', 'gk': 'open https://gameknot.com/', 'Ogk': 'open -t https://gameknot.com/', 'yt': 'open https://youtube.com', 'Oyt': 'open -t https://youtube.com', 'Y': 'open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files', 'OY': 'open -t http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files', '1337': 'open https://1337x.to/home', 'O1337': 'open -t https://1337x.to/home', 'ez': 'open https://eztv.ag', 'Oez': 'open -t https://eztv.ag', 'tz': 'open https://extratorrent.ag', 'Otz': 'open -t https://extratorrent.ag', 'tpb': 'open https://thepiratebay.org/', 'Otpb': 'open -t https://thepiratebay.org/', 'Nm': 'open https://netmail.herts.ac.uk/', 'ONm': 'open -t https://netmail.herts.ac.uk/' }
+
diff --git a/.config/qutebrowser/Archive/search.txt b/.config/qutebrowser/Archive/search.txt
new file mode 100644
index 0000000..e91a46c
--- /dev/null
+++ b/.config/qutebrowser/Archive/search.txt
@@ -0,0 +1,2 @@
+ c.url.searchengines = { 'DEFAULT': 'https://duckduckgo.com/?q={}', 'aw': 'https://wiki.archlinux.org/?q={}', 'g': 'https://www.google.com/?q={}' }
+
diff --git a/.config/qutebrowser/autoconfig.yml b/.config/qutebrowser/autoconfig.yml
new file mode 100644
index 0000000..b2419ef
--- /dev/null
+++ b/.config/qutebrowser/autoconfig.yml
@@ -0,0 +1,12 @@
+# If a config.py file exists, this file is ignored unless it's explicitly loaded
+# via config.load_autoconfig(). For more information, see:
+# https://github.com/qutebrowser/qutebrowser/blob/master/doc/help/configuring.asciidoc#loading-autoconfigyml
+# DO NOT edit this file by hand, qutebrowser will overwrite it.
+# Instead, create a config.py - see :help for details.
+
+config_version: 2
+settings:
+ content.javascript.enabled:
+ '*://*.next-episode.net/*': false
+ content.notifications:
+ https://1337x.to: false
diff --git a/.config/qutebrowser/bookmarks/urls b/.config/qutebrowser/bookmarks/urls
new file mode 100755
index 0000000..1d2c584
--- /dev/null
+++ b/.config/qutebrowser/bookmarks/urls
@@ -0,0 +1,272 @@
+https://www.ifixit.com/Guide/HP+TouchPad+Battery+Replacement/6082 HP TouchPad Battery Replacement - iFixit
+http://opensecuritytraining.info/LifeOfBinaries.html LifeOfBinaries
+http://stackoverflow.com/questions/20027990/how-can-i-get-text-section-from-pe-file-using-pefile python - How can I get .text section from PE file using pefile - Stack Overflow
+http://what-when-how.com/windows-forensic-analysis/executable-file-analysis-windows-forensic-analysis-part-2/ Executable File Analysis (Windows Forensic Analysis) Part 2
+http://stackoverflow.com/questions/2363483/python-slicing-a-very-large-binary-file Python: slicing a very large binary file - Stack Overflow
+http://stackoverflow.com/questions/39312720/python-extract-text-from-struct-or-binary-file Python extract text from Struct, or binary file - Stack Overflow
+https://www.poftut.com/use-linux-xxd-command-tutorial-hex-binary-operations-example/ How To Use Linux Xxd Command Tutorial For Hex and Binary Operations With Example? - Poftut
+http://stackoverflow.com/questions/28627349/offset-when-reading-binary-file-in-python binaryfiles - Offset when reading binary file in python - Stack Overflow
+http://stackoverflow.com/questions/18954596/python-reading-binary-file-with-offsets-and-structs Python - Reading binary file with offsets and structs - Stack Overflow
+http://stackoverflow.com/questions/3299213/python-how-can-i-open-a-file-and-specify-the-offset-in-bytes Python - How can I open a file and specify the offset in bytes? - Stack Overflow
+http://stackoverflow.com/questions/35883044/python-2-7-11-how-to-read-big-binary-file-from-at-hex-offset Python 2.7.11: How to read (big) binary file from/at hex offset - Stack Overflow
+qute://help/commands.html#unbind Commands
+https://askubuntu.com/questions/534187/where-is-the-login-screen-wallpaper-for-gdm-stored 14.04 - Where is the login screen wallpaper for GDM stored? - Ask Ubuntu
+file://${HOME}/Documents/Personal/bookmarks.html Bookmarks
+https://heasarc.gsfc.nasa.gov/ HEASARC: NASA's Archive of Data on Energetic Phenomena
+https://netmail.herts.ac.uk/ecp/?rfr=owa&owaparam=modurl%3D0&p=organize/AutomaticReplies.slab automatic replies - Outlook Web App
+http://localhost:8889/notebooks/Sandbox/Python/Untitled.ipynb# Untitled
+https://ctftime.org/ CTFtime.org / All about CTF (Capture The Flag)
+http://overthewire.org/wargames/ OverTheWire: Wargames
+https://stackoverflow.com/questions/38580647/how-to-create-a-directory-search-and-replace-with-vimscript-and-fzf vim - How to create a directory search and replace with vimscript and fzf - Stack Overflow
+https://superuser.com/questions/1314397/how-to-search-file-contents-grep-ag-rg-using-fzf How to search file *contents* (grep/ag/rg) using FZF? - Super User
+https://news.ycombinator.com/item?id=15514589 FZF and RipGrep – Navigate with bash faster than ever before | Hacker News
+https://vi.stackexchange.com/questions/14242/how-to-call-the-fzf-ag-command-with-multiple-arguments/14246 vimscript - How to call the fzf Ag command with multiple arguments? - Vi and Vim Stack Exchange
+http://eradman.com/entrproject/ entr(1)
+http://harmful.cat-v.org/software/ All software sucks
+https://suckless.org/rocks/ Stuff that rocks | suckless.org software that sucks less
+https://github.com/ranger/ranger/wiki/Official-user-guide Official user guide · ranger/ranger Wiki
+https://turses.readthedocs.io/en/latest/user/configuration.html Configuration — turses 0.3.1 documentation
+http://vimdoc.sourceforge.net/htmldoc/message.html Vim documentation: message
+http://stevelosh.com/blog/2012/10/the-homely-mutt/ The Homely Mutt / Steve Losh
+http://www.mutt.org/doc/manual/#sidebar The Mutt E-Mail Client
+http://www.samlogic.net/articles/smtp-commands-reference.htm SMTP Commands Reference (covers HELO/EHLO, MAIL, RCPT, DATA, RSET, VRFY, AUTH, STARTTLS etc)
+https://medium.com/@insecurity_92477/ssh-shenanigans-the-good-the-bad-and-the-evil-16945ca72596 SSH Shenanigans — The good, the bad, and the evil – PROJECT INSECURITY – Medium
+https://dzone.com/articles/ssh-from-the-ground-up SSH From the Ground Up - DZone Performance
+http://flask.pocoo.org/ Welcome | Flask (A Python Microframework)
+https://netmail.herts.ac.uk/owa/#path=/mail Graffagnino, Vito - Outlook Web App
+http://www.therandymon.com/woodnotes/mutt/node24.html Configuring Mutt
+http://www.therandymon.com/woodnotes/mutt/using-mutt.html The Woodnotes Guide to the Mutt Email Client
+http://www.grameen-info.org/windows-7-professional-product-key-free-for-you/ Windows 7 Professional Product Key Free | Grameen Bank - Bank For The Poor
+https://www.itechgyan.com/windows-7-professional-product-key-32-64-bit/ Windows 7 Professional Product Key for 32/64- bit - iTechGyan.com
+https://www.thewindowsclub.com/disable-auto-activation-feature-windows-7-8 Disable Automatic Windows Activation popup in Windows 10/8/7
+https://dotfiles.github.io/ GitHub does dotfiles - dotfiles.github.io
+https://developer.atlassian.com/blog/2016/02/best-way-to-store-dotfiles-git-bare-repo/ The best way to store your dotfiles: A bare Git repository - Atlassian Developers
+https://stackoverflow.com/questions/49658760/convert-json-format-file-to-tsv-using-python convert json format file to tsv using python - Stack Overflow
+https://gitlab.com/dwt1/dotfiles/blob/master/.config/vifm/vifmrc .config/vifm/vifmrc · master · Derek Taylor / dotfiles · GitLab
+https://guides.fixato.org/weechat/ FiXato's Guide to WeeChat
+https://www.linode.com/docs/applications/messaging/using-weechat-for-irc/ Using WeeChat for Internet Relay Chat
+https://hugo.md/post/the-perfect-weechat-setup-2/ The Perfect Weechat Setup · Hugo's Blog
+https://weechat.org/files/doc/devel/weechat_faq.en.html#irc_ssl_connection WeeChat FAQ (Frequently Asked Questions)
+https://forum.xda-developers.com/hp-touchpad/help/how-to-install-android-7-x-hp-touchpad-t3512182/post69927244#post69927244 [ROM GUIDE] How to Install Android 9.x/8.1.x… | HP TouchPad
+https://kevin.deldycke.com/2012/08/gmail-backup-mbsync/ Keep a Local Backup of Gmail Thanks to Mbsync | Kevin Deldycke
+https://forum.xda-developers.com/showpost.php?p=52922166&postcount=64 [TOOLS] Touchpad Toolbox (updated 2015-02-25) - Post #64
+https://forum.xda-developers.com/hp-touchpad/help/guide-video-how-to-bypass-webos-t3636750 [Guide+Video]How to bypass WebOS activation … | HP TouchPad
+https://forum.xda-developers.com/hp-touchpad/development/luneos-webos-ports-t2851450 LuneOS (webOS Ports) | HP TouchPad
+https://forum.xda-developers.com/showthread.php?t=1426244 [Guide] Factory condition restoration / Down… | HP TouchPad
+https://haveibeenpwned.com/ Have I Been Pwned: Check if your email has been compromised in a data breach
+https://forum.xda-developers.com/showthread.php?t=2761381 DebiAndroid [WIP] - Debian native/chroot for… | HP TouchPad
+https://indico.ict.inaf.it/event/720/ X-RAY ASTRONOMY 2019 (8-13 September 2019) · INDICO @ INAF (Indico)
+https://docs.google.com/forms/d/e/1FAIpQLSf388FvDduVVNT0lLpSz0KrvGjlfuALvv6jnoLBen4bEh26TQ/viewform Chandra/CIAO Workshop, Bologna, Italy, 2019
+https://www.manualslib.com/download/880661/Bmw-318i.html Download BMW 318i Owner's Handbook Manual
+https://complaint.resolver.co.uk/#/dashboard Resolver
+https://www.moneysavingexpert.com/reclaim/ppi-loan-insurance/#resolver Reclaim PPI for Free: How to claim £1000s - MoneySavingExpert
+https://eztv.io/ EZTV - TV Torrents Online Series Download | Official
+https://miloserdov.org/?p=1088 Fast and simple method to bypass Captive Portal (hotspot with authorization on the web-interface) - Ethical hacking and penetration testing
+https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png cheatsheet-big.png (3342×2060)
+https://jdhao.github.io/2018/12/24/centos_nvim_install_use_guide_en/ A Complete Guide for Installing and Setting up Neovim for Python Development - jdhao's blog
+https://stackoverflow.com/questions/54429210/how-do-i-prevent-conda-from-activating-the-base-environment-by-default bash - How do I prevent Conda from activating the base environment by default? - Stack Overflow
+https://stackoverflow.com/questions/32611932/how-can-i-switch-using-pip-between-system-and-anaconda python - How can I switch using pip between system and anaconda - Stack Overflow
+http://docs.plasmapy.org/en/latest/auto_examples/plot_dispersion_function.html#sphx-glr-download-auto-examples-plot-dispersion-function-py The plasma dispersion function — PlasmaPy 0.2.0 documentation
+http://docs.plasmapy.org/en/latest/development/install_dev.html#setting-up-an-environment-for-development Installing PlasmaPy for Development — PlasmaPy 0.2.0 documentation
+https://linuxhint.com/arch-linux-docker-tutorial/ Arch Linux Docker Tutorial – Linux Hint
+https://stackoverflow.com/questions/45692255/how-make-openvpn-work-with-docker debian - How make openvpn work with docker - Stack Overflow
+https://wiki.archlinux.org/index.php/Docker Docker - ArchWiki
+https://linuxhint.com/docker_arch_linux/ How to Use Docker on Arch Linux – Linux Hint
+https://docs.docker.com/get-started/ Orientation and setup | Docker Documentation
+https://news.ycombinator.com/item?id=11071754 I use: git init --bare $HOME/.myconf alias config='/usr/bin/git --git-d... | Hacker News
+https://www.atlassian.com/git/tutorials/dotfiles How to store dotfiles | Atlassian Git Tutorial
+https://www.cgsecurity.org/wiki/Advanced_Find_ext2_ext3_Backup_SuperBlock Advanced Find ext2 ext3 Backup SuperBlock - CGSecurity
+http://stevelosh.com/blog/2012/10/the-homely-mutt/#installing-offlineimap The Homely Mutt / Steve Losh
+https://wiki.archlinux.org/index.php/Isync isync - ArchWiki
+https://forums.webosnation.com/hp-veer/313841-webos-2-2-4-doctor-veer-t-wr-13.html webOS 2.2.4 Doctor for Veer for AT&T or WR - Page 13 - webOS Nation Forums
+https://www.webos-internals.org/ WebOS Internals
+http://android-dls.com/wiki/index.php?title=HOWTO:_Unpack%2C_Edit%2C_and_Re-Pack_Boot_Images#Unpacking.2C_Editing.2C_and_Re-Packing_the_images HOWTO: Unpack, Edit, and Re-Pack Boot Images - Android Wiki
+https://github.com/webos-internals/meta-doctor/blob/master/scripts/meta-att-touchpad-3.0.4 meta-doctor/meta-att-touchpad-3.0.4 at master · webos-internals/meta-doctor · GitHub
+https://www.webos-internals.org/wiki/WebOS_Doctor_Versions WebOS Doctor Versions - WebOS Internals
+https://bostonenginerd.com/posts/notmuch-of-a-mail-setup-part-1-mbsync-msmtp-and-systemd/ Notmuch of a mail setup Part 1- mbsync, msmtp and systemd | Assorted Nerdery
+https://blog.jpalardy.com/posts/my-best-awk-tricks/ My Best Awk Tricks | Jonathan Palardy's Blog
+https://weechat.org/files/doc/devel/weechat_user.en.html#buffers_and_windows WeeChat user’s guide
+https://www.irchelp.org/faq/irctutorial.html IRCHelp.org — An IRC Tutorial
+http://learn.astropy.org/ Learn Astropy — astropy-tutorials v3.0.dev
+https://unix.stackexchange.com/questions/315063/mount-wrong-fs-type-bad-option-bad-superblock ubuntu - mount: wrong fs type, bad option, bad superblock - Unix & Linux Stack Exchange
+https://ubuntuforums.org/showthread.php?t=839579 [ubuntu] mount: wrong fs type, bad option, bad superblock on /dev/sdb1
+https://ubuntuforums.org/showthread.php?t=1245536&p=7822694#post7822694 [SOLVED] How to recover from a bad superblock
+https://www.cyberciti.biz/tips/surviving-a-linux-filesystem-failures.html Surviving a Linux Filesystem Failures - nixCraft
+https://major.io/2010/12/14/mounting-a-raw-partition-file-made-with-dd-or-dd_rescue-in-linux/ Mounting a raw partition file made with dd or dd_rescue in Linux :: major.io
+http://www.garloff.de/kurt/linux/ddrescue/ dd_rescue
+https://jcutrer.com/howto/how-to-install-youtube-dl-the-easy-way How to install youtube-dl (the easy way) - jcutrer.com
+https://www.howtogeek.com/261591/how-to-create-and-run-bash-shell-scripts-on-windows-10/ How to Create and Run Bash Shell Scripts on Windows 10
+https://www.how2shout.com/how-to/install-youtube-dl-linux-macos.html How to install Youtube-dl on Linux and MacOS | H2S Media
+https://overthewire.org/wargames/ OverTheWire: Wargames
+https://wiki.webos-internals.org/wiki/Application:Navit Application:Navit - WebOS Internals
+https://www.linux.com/tutorials/how-fix-mangled-partition-table-linux/ How to Fix a Mangled Partition Table on Linux - Linux.com
+http://pragmaticemacs.com/emacs/migrating-from-offlineimap-to-mbsync-for-mu4e/ Migrating from offlineimap to mbsync for mu4e | Pragmatic Emacs
+https://emacs-fu.blogspot.com/2012/08/introducing-mu4e-for-email.html emacs-fu: introducing mu4e, an e-mail client for emacs
+http://cachestocaches.com/2017/3/complete-guide-email-emacs-using-mu-and-/ A Complete Guide to Email in Emacs using Mu and Mu4e
+https://www.emacswiki.org/emacs/mu4e EmacsWiki: mu4e
+https://pertuttirestaurant.co.uk/?utm_source=tripadvisor&utm_medium=referral Per Tutti Restaurant | Authentically Italian
+https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d732621-Reviews-La_Cosa_Nostra-St_Albans_Hertfordshire_England.html LA COSA NOSTRA, St Albans - Updated 2020 Restaurant Reviews, Photos & Phone Number - Tripadvisor
+http://barmeze.co.uk/booking-confirmation.html Bar Meze - St Albans | Reservation confirmation
+https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d1742312-Reviews-L_Italiana-St_Albans_Hertfordshire_England.html L'ITALIANA, St Albans - Updated 2020 Restaurant Reviews, Menu & Prices - Tripadvisor
+https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d9990284-Reviews-Per_Tutti_St_Albans-St_Albans_Hertfordshire_England.html PER TUTTI ST ALBANS - Updated 2020 Restaurant Reviews, Menu & Prices - Tripadvisor
+https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d2318820-Reviews-Alloro-St_Albans_Hertfordshire_England.html ALLORO, St Albans - Updated 2020 Restaurant Reviews, Menu, Prices & Restaurant Reviews - Food Delivery & Takeaway - Tripadvisor
+https://pertuttirestaurant.co.uk/book-berkhamsted/ BOOK BERKHAMSTED | Per Tutti Restaurant
+https://www.tripadvisor.co.uk/Restaurant_Review-g186306-d11885339-Reviews-Carmelo_s-St_Albans_Hertfordshire_England.html CARMELO'S, St Albans - Updated 2020 Restaurant Reviews, Menu & Prices - Tripadvisor
+https://dystroy.org/broot/ Broot
+http://www.ss.ncu.edu.tw/~lyu/lecture_files_en/index.html lyu_lecture_ch
+http://silas.psfc.mit.edu/introplasma/index.html Introduction to Plasma Physics
+https://www.plasma-universe.com/ The Plasma Universe
+http://farside.ph.utexas.edu/teaching/plasma/lectures/Plasmahtml.html Plasmahtml
+https://spacephysics.ucla.edu/index.html UCLA IGPP Space Physics Exercises
+https://www.slickremix.com/docs/get-api-key-for-youtube/# Get API Key for YouTube | SlickRemix
+https://courses.edx.org/courses/course-v1:EPFLx+PlasmaIntroductionX+1T_2018/course/ Course | PlasmaIntroductionX | edX
+https://www.justpark.com/dashboard/bookings/made/ Bookings | JustPark
+https://homepages.spa.umn.edu/~kd/Ast4001-2015/NOTES/n052-saha-bradt.pdf
+http://www.astro.wisc.edu/~townsend/resource/teaching/astro-310-F09/solutions-2009-10-05.pdf
+http://www.astro.wisc.edu/~townsend/resource/teaching/astro-310-F11/solutions-2011-10-14.pdf
+https://people.physics.tamu.edu/belyanin/astr314/lecture8.pdf
+http://www.astro.umd.edu/~miller/teaching/astr601/lecture08.pdf
+http://www.astro.caltech.edu/~jlc/ay123_2007/save_sol3_2007.pdf
+https://leanpub.com/the-tao-of-tmux/read Read The Tao of tmux | Leanpub
+https://www.datacamp.com/community/tutorials/tutorial-jupyter-notebook (Tutorial) Jupyter Notebook: The Definitive Guide - DataCamp
+https://github.com/jakevdp/PythonDataScienceHandbook GitHub - jakevdp/PythonDataScienceHandbook: Python Data Science Handbook: full text in Jupyter Notebooks
+https://jakevdp.github.io/PythonDataScienceHandbook/ Python Data Science Handbook | Python Data Science Handbook
+https://irkernel.github.io/installation/#binary-panel Installation · IRkernel
+https://nbviewer.jupyter.org/github/jrjohansson/scientific-python-lectures/blob/master/Lecture-4-Matplotlib.ipynb Jupyter Notebook Viewer
+https://4dcombat.thinkific.com/courses/4dcombat-online 4D Combat - Online Course
+https://www.linode.com/docs/applications/messaging/using-weechat-for-irc/# Using WeeChat for Internet Relay Chat | Linode
+https://realpython.com/courses/functional-programming-python/ Functional Programming in Python – Real Python
+https://realpython.com/courses/how-to-publish-your-own-python-package-pypi/ How to Publish Your Own Python Package to PyPI – Real Python
+https://realpython.com/courses/traditional-face-detection-python/ Traditional Face Detection With Python – Real Python
+https://realpython.com/courses/cool-new-features-python-38/ Cool New Features in Python 3.8 – Real Python
+https://realpython.com/account/purchases/ Your Purchases – Real Python
+https://weechat.org/files/doc/stable/weechat_user.en.html#usage WeeChat user’s guide
+https://weechat.org/scripts/ WeeChat :: scripts
+https://github.com/GermainZ/weechat-vimode GitHub - GermainZ/weechat-vimode: A WeeChat script that adds vi-like modes, commands and keybindings.
+https://github.com/wee-slack/wee-slack GitHub - wee-slack/wee-slack: A WeeChat plugin for Slack.com. Supports threads and reactions, synchronizes read markers, provides typing notification, etc..
+http://nemrod.se/guides/install-rpm-packages-on-arch-linux/ Install RPM packages on Arch Linux | nemrod.se
+https://github.com/jupyter/jupyter/wiki/Jupyter-kernels Jupyter kernels · jupyter/jupyter Wiki · GitHub
+https://www.docker.com/get-started Get Started with Docker | Docker
+https://neovim.io/doc/user/nvim_terminal_emulator.html Nvim documentation: nvim_terminal_emulator
+https://neovim.io/doc/user/ Nvim documentation: help
+https://github.com/seebye/ueberzug GitHub - seebye/ueberzug: ueberzug is an alternative for w3mimgdisplay
+https://github.com/seebye/fmui GitHub - seebye/fmui: fzf mpd user interface
+https://hertfordshire.voxcharta.org/?s=aurora Search results for 'aurora' (page 1 of 6)Vox Charta
+https://casa.nrao.edu/casa_obtaining.shtml CASA
+https://ads.readthedocs.io/en/latest/ The ads Python package — ads 0.12.2 documentation
+https://ui.adsabs.harvard.edu/ NASA/ADS
+https://wiki.vifm.info/index.php?title=Quickstart_Tutorial Quickstart Tutorial - Vifm Wiki
+https://arxiv.org/help/api/index arXiv API | arXiv e-print repository
+https://github.com/junegunn/fzf/wiki/examples#search-for-academic-pdfs-by-author-title-journal-institution Examples · junegunn/fzf Wiki
+https://www.reddit.com/r/neovim/comments/fu1omj/help_request_getting_deoplete_and_ultisnips_to/ [Help request] Getting deoplete and ultisnips to share completion key. : neovim
+https://github.com/junegunn/fzf#preview-window junegunn/fzf: A command-line fuzzy finder
+https://blog.thegate.ai/posts/20191113_configuring-neomutt-for-a-local-email-setup/ TheGate.AI | Blog | Creating a local email setup with mbsync + msmtp + neomutt + notmuch.
+https://bostonenginerd.com/posts/notmuch-of-a-mail-setup-part-2-notmuch-and-emacs/ Notmuch of mail a setup Part 2 - notmuch and Emacs | Assorted Nerdery
+https://notmuchmail.org/mutttips/ mutttips
+http://isync.sourceforge.net/ isync: free IMAP and MailDir mailbox synchronizer
+https://notmuchmail.org/howto/ howto
+https://neomutt.org/feature/notmuch Notmuch - NeoMutt
+https://wiki.archlinux.org/index.php/Notmuch Notmuch - ArchWiki
+https://guides.nyu.edu/c.php?g=601858&p=4168138 Templates - Getting Started with LaTeX - Research Guides at New York University
+https://www.latex-tutorial.com/tutorials/bibtex/ Bibliography in LaTeX with Bibtex/Biblatex
+https://www.math.ias.edu/computing/faq/local-latex-style-files Where can I put local latex style files and packages? | IAS School of Mathematics
+https://en.wikibooks.org/wiki/LaTeX/Installing_Extra_Packages LaTeX/Installing Extra Packages - Wikibooks, open books for an open world
+https://github.com/adsabs/adsabs-dev-api/blob/master/Search_API.ipynb adsabs-dev-api/Search_API.ipynb at master · adsabs/adsabs-dev-api
+https://github.com/bibcure/bibcure bibcure/bibcure: Bibcure helps in boring tasks by keeping your bibfile up to date and normalized...also allows you to easily download all papers inside your bibtex
+https://arxiv.org/help/api/index#using arXiv API | arXiv e-print repository
+https://github.com/junegunn/fzf/wiki/examples#buku Examples · junegunn/fzf Wiki
+https://pandoc.org/getting-started.html Pandoc - Getting started with pandoc
+https://tmuxcheatsheet.com/ Tmux Cheat Sheet & Quick Reference
+https://stackoverflow.com/questions/1550226/python-setup-py-uninstall python setup.py uninstall - Stack Overflow
+https://notmuchmail.org/initial_tagging/ initial tagging
+https://neomutt.org/guide/configuration.html#mbox-hook Configuration - NeoMutt
+http://jazz-the-two-of-us.blogspot.com/ The Sound of Jazz : Download Jazz MP3 Album
+http://cazadam.blogspot.com/p/blog-page.html CazAdam: Albums
+https://listen-jazz-easily.blogspot.com/2009/01/charlie-parker-very-best.html#links Listen Jazz Easily : Download Jazz Album on Blogspot: Charlie Parker - The Very Best
+https://catonmat.net/linux-and-vim-notes My Linux and Vim Notes
+https://bestlifeonline.com/hilariously-silly-jokes/ 40 Hilarious Jokes You Can Tell Absolutely Anyone - Silly Jokes
+https://null-byte.wonderhowto.com/ Null Byte — The aspiring white-hat hacker/security awareness playground « Null Byte :: WonderHowTo
+https://www.alfashop.co.uk/ Home | AlfaShop
+https://www.alfashop.co.uk/products/search?utf8=%E2%9C%93&f_search=manual&button= https://www.alfashop.co.uk/products/search?utf8=✓&f_search=manual&button=
+https://www.aroc-uk.com/159-register/gallery 159 Register Gallery | Alfa Romeo Owners Club
+http://www.ausalfa.com/viewtopic.php?f=4&t=9411#p157448 ausalfa.com • View topic - Installation of after-market head unit into 159/Brera/Spider
+https://www.alfaowner.com/threads/head-unit-stereo-upgrade-instructions.931370/ Head Unit / stereo Upgrade Instructions | Alfa Romeo Forum
+https://ntmlabs.com/w3m-tricks/ NTM Labs - W3M tricks of the trade
+https://github.com/felipesaa/A-vim-like-firefox-like-configuration-for-w3m/blob/master/documentation/functions.txt A-vim-like-firefox-like-configuration-for-w3m/functions.txt at master · felipesaa/A-vim-like-firefox-like-configuration-for-w3m
+https://shells.red-pill.eu/ Free Shell Accounts :: the biggest list on the net - Rankings - All Sites
+https://sdf.org/ SDF Public Access UNIX System - Free Shell Account and Shell Access
+http://www.nyx.net/ Nyx
+https://www.chrisatmachine.com/ Christian Chiarulli
+https://www.manualslib.com/manual/628233/Alfa-Romeo-159.html ALFA ROMEO 159 OWNER'S MANUAL Pdf Download.
+https://www.howtogeek.com/358166/using-android-without-google-a-kind-of-guide/ Using Android without Google: A (Kind of) Guide
+https://android.stackexchange.com/questions/91628/how-to-remove-all-google-specific-apps-from-a-rooted-android applications - How to remove all Google-specific apps from a rooted Android? - Android Enthusiasts Stack Exchange
+https://www.ducksters.com/jokes/silly.php Jokes for kids: big list of clean silly jokes
+https://www.vpscheap.net/pricing.aspx Unmetered SSD and Budget VPS Hosting | VPSCheap.NET
+https://www.thinkmoney.co.uk/credit-card/ Credit Cards UK | Apply for a Credit Card Online
+https://tablesgenerator.com/ Create LaTeX tables online – TablesGenerator.com
+http://detexify.kirelabs.org/classify.html Detexify LaTeX handwritten symbol recognition
+https://outlook.office.com/mail/inbox Email - Vito Graffagnino - Outlook
+https://www.androidjungles.com/how-to-unlock-bootloader-on-samsung-phones/ How to Unlock Bootloader on Samsung Phones
+https://forum.xda-developers.com/galaxy-j5/samsung-galaxy-j5-2016-roms-kernels-recoveries--other-development/rom-tw-wycked-os-v2-0-updated-11-05-2018-t3789168 [ROM TW][7.1.1][J5 2016] Wycked-OS v3.0 Fina… | Samsung Galaxy J5
+https://forum.xda-developers.com/galaxy-j5/samsung-galaxy-j5-2016-roms-kernels-recoveries--other-development/rom-resurrection-remix-j5-2016-t3955181 [ROM][9]Resurrection Remix For J5 2016 | Samsung Galaxy J5
+https://www.getdroidtips.com/samsung-galaxy-j5-2016-stock-firmware/ Samsung Galaxy J5 2016 Stock Firmware Collections
+https://forum.xda-developers.com/galaxy-nexus/guide-phone-backup-unlock-root-t1420351 [GUIDE] Full Phone Backup without Unlock or … |
+https://appuals.com/fix-downloading-not-turn-off-target/ Fix: Downloading... Do not turn off target - Appuals.com
+https://awesomedetect.com/flash-recovery-samsung-j5-2016-nougat-7-1-1/ Flash TWRP recovery in Samsung J5 2016 Nougat 7.1.1 without root – Awesome detect
+https://www.droidviews.com/how-to-extract-pit-file-from-samsung-galaxy-devices/ Download Samsung PIT Files and Extract Them on Samsung Devices
+https://www.full-repair-firmware.com/2017/10/full-firmware-j510fn.html Full Firmware For Device Samsung Galaxy J5 2016 SM-J510FN
+https://www.full-repair-firmware.com/p/pit-files.html Pit Files
+https://gearallnews.com/how-to-root-galaxy-j5-2016-sm-j510fn-and-install-twrp/ How To Root Galaxy J5 2016 SM-J510FN and Install TWRP
+https://www.androidjungles.com/how-to-boot-into-fastboot-mode-on-samsung-phone/ How to Boot Into Fastboot Mode on Samsung Phone
+https://www.androidsage.com/2017/07/12/list-of-samsung-galaxy-country-specific-product-code-csc-and-country-region/ List of Samsung Galaxy CSC Country Specific Product code and region they belong to
+https://www.droidwin.com/root-android-devices-without-twrp-recovery/ How to Root Android Devices Without TWRP Recovery
+https://rootmygalaxy.net/download-odin-tool-samsung-galaxy-devices-versions/ Download Samsung Odin Flash Tool (All Versions) for Windows
+https://www.getdroidtips.com/best-magisk-modules-2020/ Best Magisk Modules You Should Try in 2020
+https://android.gadgethacks.com/how-to/install-microg-replace-google-play-services-prevent-data-mining-0193787/ How to Install MicroG to Replace Google Play Services & Prevent Data Mining « Android :: Gadget Hacks
+https://acmarket.net/ ACMarket - Cracked Apps, Games, Mods for Android
+https://apkmody.io/apps/titanium-backup-pro Titanium Backup Pro APK 8.4.0.2 (MOD Unlocked) Download
+https://microg.org/download.html Download - microG Project
+http://hex.ro/wp/blog/removing-gapps-from-cyanogenmod-11/ Removing gapps from Cyanogenmod 11 – and thus goes by another day
+https://www.coinbase.com/join/smith_5to1 Buy/Sell cryptocurrency - Coinbase
+https://www.vultr.com/features/datacenter-locations/#locations-map Largest Cloud Server Network Available - Vultr.com
+https://my.vultr.com/ Log In - Vultr.com
+https://stackpointer.io/mobile/android-adb-backup-extract-restore-repack/372/ Android ADB Backup, Extract, Restore - Stack Pointer
+https://www.reddit.com/r/linux/comments/13nuda/poor_mans_vpn_with_ssh/ Poor man's VPN with ssh : linux
+https://www.reddit.com/r/linux/comments/13nuda/poor_mans_vpn_with_ssh/?sort=new Poor man's VPN with ssh : linux
+https://appmaildev.com/en/dkim DKIM Test - DKIM Verify - DKIM Validator
+https://www.williamjbowman.com/blog/2015/07/24/setting-up-webdav-caldav-and-carddav-servers/ Setting up WebDAV, CalDAV, and CardDAV servers
+https://jonathantutorial.blogspot.com/2014/10/how-to-set-up-radicale.html Jonathan Tutorials: How to set up a private Calendar and Contacts server (Radicale) on Ubuntu 14.04 LTS
+https://doom.fandom.com/wiki/Doom_cheat_codes Doom cheat codes | Doom Wiki | Fandom
+https://www.reddit.com/r/Piracy/comments/baufql/youtubedl_the_easy_way_on_android/ YouTube-DL the easy way on Android : Piracy
+https://wiki.termux.com/wiki/Graphical_Environment Graphical Environment - Termux Wiki
+https://www.techncyber.com/2019/08/kali-nethunter-in-termux.html How To use Kali Nethunter in Termux
+https://www.reddit.com/r/linux/comments/66fh4f/what_do_you_use_termux_on_android_for/ What do you use Termux on android for? : linux
+https://build.nethunter.com/kalifs/kalifs-latest/ Index of /kalifs/kalifs-latest/
+https://github.com/hiway/python-qutescript hiway/python-qutescript: Painless userscripts for qutebrowser.
+qute://help/userscripts.html Writing qutebrowser userscripts
+https://newsboat.org/releases/2.20.1/docs/newsboat.html#_query_feeds The Newsboat RSS Feedreader
+https://hiphish.github.io/blog/2020/05/31/macho-man-command-on-steroids/ HiPhish's Workshop
+https://www.verizon.com/support/knowledge-base-221536/ Palm - Factory Data Reset (Powered Off) | Verizon
+https://devhints.io/bash Bash scripting cheatsheet
+https://forum.xda-developers.com/galaxy-j5/samsung-galaxy-j5-2016-roms-kernels-recoveries--other-development/patcher-dualbootpatcher-j510-t3614376/page7 [PATCHER] DualBootPatcher for J510 - Pg. 7 | Samsung Galaxy J5
+https://new.myhermes.co.uk/track.html#/parcel/8017376759340718/details Track a parcel - MyHermes
+https://forum.xda-developers.com/galaxy-j5/samsung-galaxy-j5-2016-roms-kernels-recoveries--other-development/rom-skies-a5ux-port-j510xx-hpm8294-t3586467 | ROM | | 6.0.X - 7.X.X | | J5 2016 all vari… | Samsung Galaxy J5
+https://musicasacra.com/music/latin-settings/ Latin Chant and Choral Music for the Mass | Church Music Association of America
+http://docs.adaptivecomputing.com/torque/3-0-5/commands/qdel.php qdel
+http://docs.adaptivecomputing.com/maui/commands/mjobctl.php#jobexp docs.adaptivecomputing.com/maui/commands/mjobctl.php#jobexp
+http://docs.adaptivecomputing.com/torque/2-5-12/help.htm#topics/commands/qdel.htm qdel
+https://leathermissalcovers.co.uk/index.php?route=product/product&path=61&product_id=82&limit=100 zipped leather cover
+https://webgefrickel.de/blog/a-modern-mutt-setup-part-two A modern mutt setup — part two | webgefrickel
+https://outlook.office.com/mail/options/mail/automaticReplies/automaticRepliesOption Email - Vito Graffagnino - Outlook
+http://joshwalawender.github.io/IRAFtutorial/index.html Introduction to IRAF
+http://iraf.noao.edu/tutorials/tutorials.html IRAF TUTORIALS AND EXERCISES
+https://mkiminki.github.io/notes/irafinstall.html How To Install IRAF | Megan Kiminki
+http://iraf.noao.edu/docs/src/pcix/unixsmg-toc.html#TOC UNIX/IRAF Site Manager's Guide
+https://karlcordes.com/coreboot-x220/ Karl Cordes - How to flash coreboot on Lenovo X220
+https://leathermissalcovers.co.uk/index.php?route=common/home Leather Missal Covers
+https://www.ifixit.com/Device/Lenovo_Thinkpad_X220 Lenovo Thinkpad X220 - iFixit
+https://twrp.me/faq/datamedia.html What is a data/media device?
+https://forum.xda-developers.com/android/general/guide-degoogle-device-install-microg-t4058743 [GUIDE] DeGoogle any device and install Micr… | Android Development and Hacking
+https://herts365.sharepoint.com/sites/Toolkits Toolkits - Home
+https://www.jeffgeerling.com/blogs/jeff-geerling/push-your-git-repositories Push your Git repositories to a central server, in a bare repository | Jeff Geerling
+https://python-gitlab.readthedocs.io/en/stable/cli.html gitlab CLI usage — python-gitlab 2.5.0 documentation
diff --git a/.config/qutebrowser/config.py b/.config/qutebrowser/config.py
new file mode 100644
index 0000000..742d6e1
--- /dev/null
+++ b/.config/qutebrowser/config.py
@@ -0,0 +1,1802 @@
+## Autogenerated config.py ## Documentation: ## qute://help/configuring.html ## qute://help/settings.html
+#
+# Read ~/.Xresources file for colours (see the help file listed above)
+# import subprocess
+
+# def read_xresources(prefix):
+ # props = {}
+ # x=subprocess.run(['xrdb','-query'], stdout=subprocess.PIPE)
+ # lines = x.stdout.decode().split('\n')
+ # for line in filter(lambda l : l.startswith(prefix), lines):
+ # prop, _, value = line.partition(':\t')
+ # props[prop] = value
+ # return props
+
+# xresources = read_xresources('*')
+# c.colors.statusbar.normal.bg = xresources['*.background']
+
+
+## This is here so configs done via the GUI are still loaded.
+## Remove it to not load settings done via the GUI.
+# config.load_autoconfig()
+
+## Aliases for commands. The keys of the given dictionary are the
+## aliases, while the values are the commands they map to.
+## Type: Dict
+
+c.aliases = {'w':'session-save', 'q':'quit', 'wq':'quit --save',
+ '1337':'open https://1337x.to/home/',
+ 'O1337':'open -t https://1337x.to/home/',
+ 'ads':'open https://ui.adsabs.harvard.edu/classic-form',
+ 'Oads':'open -t https://ui.adsabs.harvard.edu/classic-form',
+ 'an':'open https://neonalley.com',
+ 'Oan':'open -t https://neonalley.com',
+ 'am':'open https://amazon.co.uk',
+ 'Oam':'open -t https://amazon.co.uk',
+ 'bc':'open https://bitchute.com',
+ 'Obc':'open -t https://bitchute.com',
+ 'bs':'open https://brittssoulsp.blogspot.com',
+ 'Obs':'open -t https://brittssoulsp.blogspot.com',
+ 'bt':'open https://bitsnoop.com',
+ 'Obt':'open -t https://bitsnoop.com',
+ 'car':'open https://car.herts.ac.uk',
+ 'Ocar':'open -t https://car.herts.ac.uk',
+ 'cluster':'open https://uhhpc.herts.ac.uk/wiki/index.php/Main_Page',
+ 'Ocluster':'open -t https://uhhpc.herts.ac.uk/wiki/index.php/Main_Page',
+ 'cm':'open https://churchmilitant.com/ ;; fake-key --global tSH ;; fake-key --global tSH ',
+ 'Ocm':'open -t https://churchmilitant.com/ ;; fake-key --global tSH ;; fake-key --global tSH ',
+ 'do':'open https://www.divinumofficium.com/',
+ 'Odo':'open -t https://www.divinumofficium.com/',
+ 'eby':'open https://ebay.co.uk',
+ 'Oeby':'open -t https://ebay.co.uk',
+ 'epk':'open https://registrar.epik.com',
+ 'Oepk':'open -t https://registrar.epik.com',
+ 'ez':'open https://eztv.ag',
+ 'Oez':'open -t https://eztv.ag',
+ 'et':'open https://extratorrent.ag/',
+ 'Oet':'open -t https://extratorrent.ag/',
+ 'fp':'open https://fmapulse.com',
+ 'Ofp':'open -t https://fmapulse.com',
+ 'gh':'open https://geekprank.com/hacker/',
+ 'Ogh':'open -t https://geekprank.com/hacker/',
+ 'gfgf':'open https://www.giffgaff.com/auth/login',
+ 'Ogfgf':'open -t https://www.giffgaff.com/auth/login',
+ 'gk':'open https://gameknot.com/',
+ 'Ogk':'open -t https://gameknot.com/',
+ 'gn':'open https://uhhpc.herts.ac.uk/ganglia/',
+ 'Ogn':'open -t https://uhhpc.herts.ac.uk/ganglia/',
+ 'helpdesk':'open https://helpdesk.herts.ac.uk',
+ 'Ohelpdesk':'open -t https://helpdesk.herts.ac.uk',
+ 'hertshub':'open https://herts365.sharepoint.com/sites/HertsHub',
+ 'Ohertshub':'open -t https://herts365.sharepoint.com/sites/HertsHub',
+ 'hx':'open https://heasarc.gsfc.nasa.gov/',
+ 'Ohx':'open -t https://heasarc.gsfc.nasa.gov/',
+ 'ia':'open http://italiaautoparts.co.uk',
+ 'Oia':'open -t http://italiaautoparts.co.uk',
+ 'iso':'open https://isohunt.to',
+ 'Oiso':'open -t https://isohunt.to',
+ 'll':'open https://go.herts.ac.uk/LiL',
+ 'Oll':'open -t https://go.herts.ac.uk/LiL',
+ 'ls':'open https://lukesmith.xyz',
+ 'Ols':'open -t https://lukesmith.xyz',
+ 'lms':'open https://lms.org.uk',
+ 'Olms':'open -t https://lms.org.uk',
+ 'lt':'open https://www.limetorrents.cc',
+ 'Olt':'open -t https://www.limetorrents.cc',
+ 'mbt':'open https://signin1.bt.com/login/emailloginform',
+ 'Ombt':'open -t https://signin1.bt.com/login/emailloginform',
+ 'netgear':'open http://2.96.233.114:8080',
+ 'Onetgear':'open -t http://2.96.233.114:8080',
+ 'Nm':'open https://outlook.office365.com/',
+ 'ONm':'open -t https://outlook.office365.com/',
+ 'nx':'open https://next-episode.net/ ;; fake-key --global tSH ;; fake-key --global tSH ',
+ 'Onx':'open -t https://next-episode.net/ ;; fake-key --global tSH ;; fake-key --global tSH ',
+ 'pm':'open https://mail.protonmail.com/login',
+ 'Opm':'open -t https://mail.protonmail.com/login',
+ 'pv':'open https://www.pivotce.com/',
+ 'Opv':'open -t https://www.pivotce.com/',
+ 'qtb':'open https://www.qutebrowser.org/',
+ 'Oqtb':'open -t https://www.qutebrowser.org/',
+ 'qtc':'open https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png',
+ 'Oqtc':'open -t https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png',
+ 'rb':'open https://www.rarbg.to',
+ 'Orb':'open -t https://www.rarbg.to',
+ 'sms':'open https://freesmscode.com',
+ 'Osms':'open -t https://freesmscode.com',
+ 'sn':'open https://studynet.herts.ac.uk',
+ 'Osn':'open -t https://studynet.herts.ac.uk',
+ 'sp':'open https://stpaulcenter.com',
+ 'Osp':'open -t https://stpaulcenter.com',
+ 't1':'open https://uhfinance.t1cloud.com',
+ 'Ot1':'open -t https://uhfinance.t1cloud.com',
+ 'tb':'open https://www.torrentbit.net',
+ 'Otb':'open -t https://www.torrentbit.net',
+ 'tf':'open https://www.torrentfunk.com',
+ 'Otf':'open -t https://www.torrentfunk.com',
+ 'tl':'open https://www.torlock.com',
+ 'Otl':'open -t https://www.torlock.com',
+ 'tpb':'open https://thepiratebay.org/',
+ 'Otpb':'open -t https://thepiratebay.org/',
+ 'tz':'open https://torrentz2.eu/my',
+ 'Otz':'open -t https://torrentz2.eu/my',
+ 'vx':'open https://hertfordshire.voxcharta.org/',
+ 'Ovx':'open -t https://hertfordshire.voxcharta.org/',
+ 'vtr':'open https://my.vultr.com/',
+ 'Ovtr':'open -t https://my.vultr.com/',
+ 'wn':'open https://www.webosnation.com/',
+ 'Own':'open -t https://www.webosnation.com/',
+ 'Y':'open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files',
+ 'OY':'open -t http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files',
+ 'yfy':'open https://yts.am/',
+ 'Oyfy':'open -t https://yts.am/',
+ 'ytn':'open https://youtube.com/?disable_polymer=1',
+ 'Oytn':'open -t https://youtube.com/?disable_polymer=1',
+ 'yt':'open https://youtube.com/',
+ 'Oyt':'open -t https://youtube.com/',
+ 'rtr':'open -t http://192.168.1.254/home.htm ;; fake-key --global tSH '}
+#
+## Time interval (in milliseconds) between auto-saves of
+## config/cookies/etc.
+## Type: Int
+# c.auto_save.interval = 15000
+
+## Always restore open sites when qutebrowser is reopened.
+## Type: Bool
+c.auto_save.session = True
+
+## Backend to use to display websites. qutebrowser supports two different
+## web rendering engines / backends, QtWebKit and QtWebEngine. QtWebKit
+## was discontinued by the Qt project with Qt 5.6, but picked up as a
+## well maintained fork: https://github.com/annulen/webkit/wiki -
+## qutebrowser only supports the fork. QtWebEngine is Qt's official
+## successor to QtWebKit. It's slightly more resource hungry than
+## QtWebKit and has a couple of missing features in qutebrowser, but is
+## generally the preferred choice.
+## Type: String
+## Valid values:
+## - webengine: Use QtWebEngine (based on Chromium).
+## - webkit: Use QtWebKit (based on WebKit, similar to Safari).
+# c.backend = 'webengine'
+
+## This setting can be used to map keys to other keys. When the key used
+## as dictionary-key is pressed, the binding for the key used as
+## dictionary-value is invoked instead. This is useful for global
+## remappings of keys, for example to map Ctrl-[ to Escape. Note that
+## when a key is bound (via `bindings.default` or `bindings.commands`),
+## the mapping is ignored.
+## Type: Dict
+# c.bindings.key_mappings = {'<Ctrl-[>': '<Escape>', '<Ctrl-6>': '<Ctrl-^>', '<Ctrl-M>': '<Return>', '<Ctrl-J>': '<Return>', '<Shift-Return>': '<Return>', '<Enter>': '<Return>', '<Shift-Enter>': '<Return>', '<Ctrl-Enter>': '<Ctrl-Return>'}
+## Background color of the completion widget category headers.
+## Type: QssColor
+# c.colors.completion.category.bg = 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050)'
+
+## Bottom border color of the completion widget category headers.
+## Type: QssColor
+# c.colors.completion.category.border.bottom = 'black'
+
+## Top border color of the completion widget category headers.
+## Type: QssColor
+# c.colors.completion.category.border.top = 'black'
+
+## Foreground color of completion widget category headers.
+## Type: QtColor
+# c.colors.completion.category.fg = 'white'
+
+## Background color of the completion widget for even rows.
+## Type: QssColor
+# c.colors.completion.even.bg = '#333333'
+
+## Text color of the completion widget. May be a single color to use for
+## all columns or a list of three colors, one for each column.
+## Type: List of QtColor, or QtColor
+# c.colors.completion.fg = ['white', 'white', 'white']
+
+## Background color of the selected completion item.
+## Type: QssColor
+# c.colors.completion.item.selected.bg = '#e8c000'
+
+## Bottom border color of the selected completion item.
+## Type: QssColor
+# c.colors.completion.item.selected.border.bottom = '#bbbb00'
+
+## Top border color of the completion widget category headers.
+## Type: QssColor
+# c.colors.completion.item.selected.border.top = '#bbbb00'
+
+## Foreground color of the selected completion item.
+## Type: QtColor
+# c.colors.completion.item.selected.fg = 'black'
+
+## Foreground color of the matched text in the completion.
+## Type: QssColor
+# c.colors.completion.match.fg = '#ff4444'
+
+## Background color of the completion widget for odd rows.
+## Type: QssColor
+# c.colors.completion.odd.bg = '#444444'
+
+## Color of the scrollbar in the completion view.
+## Type: QssColor
+# c.colors.completion.scrollbar.bg = '#333333'
+
+## Color of the scrollbar handle in the completion view.
+## Type: QssColor
+# c.colors.completion.scrollbar.fg = 'white'
+
+## Background color for the download bar.
+## Type: QssColor
+# c.colors.downloads.bar.bg = 'black'
+
+## Background color for downloads with errors.
+## Type: QtColor
+# c.colors.downloads.error.bg = 'red'
+
+## Foreground color for downloads with errors.
+## Type: QtColor
+# c.colors.downloads.error.fg = 'white'
+
+## Color gradient start for download backgrounds.
+## Type: QtColor
+# c.colors.downloads.start.bg = '#0000aa'
+
+## Color gradient start for download text.
+## Type: QtColor
+# c.colors.downloads.start.fg = 'white'
+
+## Color gradient stop for download backgrounds.
+## Type: QtColor
+# c.colors.downloads.stop.bg = '#00aa00'
+
+## Color gradient end for download text.
+## Type: QtColor
+# c.colors.downloads.stop.fg = 'white'
+
+## Color gradient interpolation system for download backgrounds.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+# c.colors.downloads.system.bg = 'rgb'
+
+## Color gradient interpolation system for download text.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+# c.colors.downloads.system.fg = 'rgb'
+
+## Background color for hints. Note that you can use a `rgba(...)` value
+## for transparency.
+## Type: QssColor
+# c.colors.hints.bg = 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8))'
+
+## Font color for hints.
+## Type: QssColor
+# c.colors.hints.fg = 'black'
+
+## Font color for the matched part of hints.
+## Type: QssColor
+# c.colors.hints.match.fg = 'green'
+
+## Background color of the keyhint widget.
+## Type: QssColor
+# c.colors.keyhint.bg = 'rgba(0, 0, 0, 80%)'
+
+## Text color for the keyhint widget.
+## Type: QssColor
+# c.colors.keyhint.fg = '#FFFFFF'
+
+## Highlight color for keys to complete the current keychain.
+## Type: QssColor
+# c.colors.keyhint.suffix.fg = '#FFFF00'
+
+## Background color of an error message.
+## Type: QssColor
+# c.colors.messages.error.bg = 'red'
+
+## Border color of an error message.
+## Type: QssColor
+# c.colors.messages.error.border = '#bb0000'
+
+## Foreground color of an error message.
+## Type: QssColor
+# c.colors.messages.error.fg = 'white'
+
+## Background color of an info message.
+## Type: QssColor
+# c.colors.messages.info.bg = 'black'
+
+## Border color of an info message.
+## Type: QssColor
+# c.colors.messages.info.border = '#333333'
+
+## Foreground color of an info message.
+## Type: QssColor
+# c.colors.messages.info.fg = 'white'
+
+## Background color of a warning message.
+## Type: QssColor
+# c.colors.messages.warning.bg = 'darkorange'
+
+## Border color of a warning message.
+## Type: QssColor
+# c.colors.messages.warning.border = '#d47300'
+
+## Foreground color of a warning message.
+## Type: QssColor
+# c.colors.messages.warning.fg = 'white'
+
+## Background color for prompts.
+## Type: QssColor
+# c.colors.prompts.bg = '#444444'
+
+## Border used around UI elements in prompts.
+## Type: String
+# c.colors.prompts.border = '1px solid gray'
+
+## Foreground color for prompts.
+## Type: QssColor
+# c.colors.prompts.fg = 'white'
+
+## Background color for the selected item in filename prompts.
+## Type: QssColor
+# c.colors.prompts.selected.bg = 'grey'
+
+## Background color of the statusbar in caret mode.
+## Type: QssColor
+# c.colors.statusbar.caret.bg = 'purple'
+
+## Foreground color of the statusbar in caret mode.
+## Type: QssColor
+# c.colors.statusbar.caret.fg = 'white'
+
+## Background color of the statusbar in caret mode with a selection.
+## Type: QssColor
+# c.colors.statusbar.caret.selection.bg = '#a12dff'
+
+## Foreground color of the statusbar in caret mode with a selection.
+## Type: QssColor
+# c.colors.statusbar.caret.selection.fg = 'white'
+
+## Background color of the statusbar in command mode.
+## Type: QssColor
+# c.colors.statusbar.command.bg = 'black'
+
+## Foreground color of the statusbar in command mode.
+## Type: QssColor
+# c.colors.statusbar.command.fg = 'white'
+
+## Background color of the statusbar in private browsing + command mode.
+## Type: QssColor
+# c.colors.statusbar.command.private.bg = 'grey'
+
+## Foreground color of the statusbar in private browsing + command mode.
+## Type: QssColor
+# c.colors.statusbar.command.private.fg = 'white'
+
+## Background color of the statusbar in insert mode.
+## Type: QssColor
+# c.colors.statusbar.insert.bg = 'darkgreen'
+
+## Foreground color of the statusbar in insert mode.
+## Type: QssColor
+# c.colors.statusbar.insert.fg = 'white'
+
+## Background color of the statusbar.
+## Type: QssColor
+# c.colors.statusbar.normal.bg = 'black'
+
+## Foreground color of the statusbar.
+## Type: QssColor
+# c.colors.statusbar.normal.fg = 'white'
+
+## Background color of the statusbar in passthrough mode.
+## Type: QssColor
+# c.colors.statusbar.passthrough.bg = 'darkblue'
+
+## Foreground color of the statusbar in passthrough mode.
+## Type: QssColor
+# c.colors.statusbar.passthrough.fg = 'white'
+
+## Background color of the statusbar in private browsing mode.
+## Type: QssColor
+# c.colors.statusbar.private.bg = '#666666'
+
+## Foreground color of the statusbar in private browsing mode.
+## Type: QssColor
+# c.colors.statusbar.private.fg = 'white'
+
+## Background color of the progress bar.
+## Type: QssColor
+# c.colors.statusbar.progress.bg = 'white'
+
+## Foreground color of the URL in the statusbar on error.
+## Type: QssColor
+# c.colors.statusbar.url.error.fg = 'orange'
+
+## Default foreground color of the URL in the statusbar.
+## Type: QssColor
+# c.colors.statusbar.url.fg = 'white'
+
+## Foreground color of the URL in the statusbar for hovered links.
+## Type: QssColor
+# c.colors.statusbar.url.hover.fg = 'aqua'
+
+## Foreground color of the URL in the statusbar on successful load
+## (http).
+## Type: QssColor
+# c.colors.statusbar.url.success.http.fg = 'white'
+
+## Foreground color of the URL in the statusbar on successful load
+## (https).
+## Type: QssColor
+# c.colors.statusbar.url.success.https.fg = 'lime'
+
+## Foreground color of the URL in the statusbar when there's a warning.
+## Type: QssColor
+# c.colors.statusbar.url.warn.fg = 'yellow'
+
+## Background color of the tab bar.
+## Type: QtColor
+# c.colors.tabs.bar.bg = '#555555'
+
+## Background color of unselected even tabs.
+## Type: QtColor
+# c.colors.tabs.even.bg = 'darkgrey'
+
+## Foreground color of unselected even tabs.
+## Type: QtColor
+# c.colors.tabs.even.fg = 'white'
+
+## Color for the tab indicator on errors.
+## Type: QtColor
+# c.colors.tabs.indicator.error = '#ff0000'
+
+## Color gradient start for the tab indicator.
+## Type: QtColor
+# c.colors.tabs.indicator.start = '#0000aa'
+
+## Color gradient end for the tab indicator.
+## Type: QtColor
+# c.colors.tabs.indicator.stop = '#00aa00'
+
+## Color gradient interpolation system for the tab indicator.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+# c.colors.tabs.indicator.system = 'rgb'
+
+## Background color of unselected odd tabs.
+## Type: QtColor
+# c.colors.tabs.odd.bg = 'grey'
+
+## Foreground color of unselected odd tabs.
+## Type: QtColor
+# c.colors.tabs.odd.fg = 'white'
+
+## Background color of selected even tabs.
+## Type: QtColor
+# c.colors.tabs.selected.even.bg = 'black'
+
+## Foreground color of selected even tabs.
+## Type: QtColor
+# c.colors.tabs.selected.even.fg = 'white'
+
+## Background color of selected odd tabs.
+## Type: QtColor
+# c.colors.tabs.selected.odd.bg = 'black'
+
+## Foreground color of selected odd tabs.
+## Type: QtColor
+# c.colors.tabs.selected.odd.fg = 'white'
+
+## Background color for webpages if unset (or empty to use the theme's
+## color).
+## Type: QtColor
+# c.colors.webpage.bg = 'white'
+
+## Number of commands to save in the command history. 0: no history / -1:
+## unlimited
+## Type: Int
+# c.completion.cmd_history_max_items = 100
+
+## Delay (in milliseconds) before updating completions after typing a
+## character.
+## Type: Int
+# c.completion.delay = 0
+
+## Height (in pixels or as percentage of the window) of the completion.
+## Type: PercOrInt
+# c.completion.height = '50%'
+
+## Minimum amount of characters needed to update completions.
+## Type: Int
+# c.completion.min_chars = 1
+
+## Move on to the next part when there's only one possible completion
+## left.
+## Type: Bool
+# c.completion.quick = True
+
+## Padding (in pixels) of the scrollbar handle in the completion window.
+## Type: Int
+# c.completion.scrollbar.padding = 2
+
+## Width (in pixels) of the scrollbar in the completion window.
+## Type: Int
+# c.completion.scrollbar.width = 12
+
+## When to show the autocompletion window.
+## Type: String
+## Valid values:
+## - always: Whenever a completion is available.
+## - auto: Whenever a completion is requested.
+## - never: Never.
+# c.completion.show = 'always'
+
+## Shrink the completion to be smaller than the configured size if there
+## are no scrollbars.
+## Type: Bool
+# c.completion.shrink = False
+
+## Format of timestamps (e.g. for the history completion).
+## Type: TimestampTemplate
+# c.completion.timestamp_format = '%Y-%m-%d'
+
+## Execute the best-matching command on a partial match.
+## Type: Bool
+c.completion.use_best_match = True
+
+## Number of URLs to show in the web history. 0: no history / -1:
+## unlimited
+## Type: Int
+# c.completion.web_history_max_items = -1
+
+## Require a confirmation before quitting the application.
+## Type: ConfirmQuit
+## Valid values:
+## - always: Always show a confirmation.
+## - multiple-tabs: Show a confirmation if multiple tabs are opened.
+## - downloads: Show a confirmation if downloads are running
+## - never: Never show a confirmation.
+# c.confirm_quit = ['never']
+
+## 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.
+## Type: Bool
+# c.content.cache.appcache = True
+
+## Maximum number of pages to hold in the global memory page cache. The
+## page cache allows for a nicer user experience when navigating forth or
+## back to pages in the forward/back history, by pausing and resuming up
+## to _n_ pages. For more information about the feature, please refer to:
+## http://webkit.org/blog/427/webkit-page-cache-i-the-basics/
+## Type: Int
+# c.content.cache.maximum_pages = 0
+
+## Size (in bytes) of the HTTP network cache. Null to use the default
+## value. With QtWebEngine, the maximum supported value is 2147483647 (~2
+## GB).
+## Type: Int
+# c.content.cache.size = None
+
+## Which cookies to accept.
+## Type: String
+## Valid values:
+## - all: Accept all cookies.
+## - no-3rdparty: Accept cookies from the same origin only.
+## - no-unknown-3rdparty: Accept cookies from the same origin only, unless a cookie is already set for the domain.
+## - never: Don't accept cookies at all.
+# c.content.cookies.accept = 'no-3rdparty'
+
+## Store cookies. Note this option needs a restart with QtWebEngine on Qt
+## < 5.9.
+## Type: Bool
+c.content.cookies.store = True
+
+## Default encoding to use for websites. The encoding must be a string
+## describing an encoding such as _utf-8_, _iso-8859-1_, etc.
+## Type: String
+# c.content.default_encoding = 'iso-8859-1'
+
+## Enable extra tools for Web developers. This needs to be enabled for
+## `:inspector` to work and also adds an _Inspect_ entry to the context
+## menu. For QtWebEngine, see `--enable-webengine-inspector` in
+## `qutebrowser --help` instead.
+## Type: Bool
+## c.content.developer_extras = True
+
+## Try to pre-fetch DNS entries to speed up browsing.
+## Type: Bool
+c.content.dns_prefetch = True
+
+## Expand each subframe to its contents. This will flatten all the frames
+## to become one scrollable page.
+## Type: Bool
+# c.content.frame_flattening = False
+
+## Allow websites to request geolocations.
+## Type: BoolAsk
+## Valid values:
+## - true
+## - false
+## - ask
+c.content.geolocation = False
+
+## Value to send in the `Accept-Language` header.
+## Type: String
+# c.content.headers.accept_language = 'en-US,en'
+
+## Custom headers for qutebrowser HTTP requests.
+## Type: Dict
+# c.content.headers.custom = {}
+
+## 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.
+## Type: Bool
+# c.content.headers.do_not_track = True
+
+## When to send the Referer header. The Referer header tells websites
+## from which website you were coming from when visiting them.
+## Type: String
+## Valid values:
+## - always: Always send the Referer.
+## - never: Never send the Referer. This is not recommended, as some sites may break.
+## - same-domain: Only send the Referer for the same domain. This will still protect your privacy, but shouldn't break any sites.
+# c.content.headers.referer = 'same-domain'
+
+## User agent to send. Unset to send the default.
+## Type: String
+# c.content.headers.user_agent = None
+
+## Enable host blocking.
+## Type: Bool
+# c.content.host_blocking.enabled = True
+
+## List of URLs of lists which contain hosts to block. The file can be
+## in one of the following formats: - An `/etc/hosts`-like file - One
+## host per line - A zip-file of any of the above, with either only one
+## file, or a file named `hosts` (with any extension).
+## Type: List of Url
+# c.content.host_blocking.lists = ['https://www.malwaredomainlist.com/hostslist/hosts.txt', 'http://someonewhocares.org/hosts/hosts', 'http://winhelp2002.mvps.org/hosts.zip', 'http://malwaredomains.lehigh.edu/files/justdomains.zip', 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext']
+
+## List of domains that should always be loaded, despite being ad-
+## blocked. Domains may contain * and ? wildcards and are otherwise
+## required to exactly match the requested domain. Local domains are
+## always exempt from hostblocking.
+## Type: List of String
+c.content.host_blocking.whitelist = ['piwik.org','next-episode.net','churchmilitant.com','churchmilitant.tv']
+
+## Enable hyperlink auditing (`<a ping>`).
+## Type: Bool
+# c.content.hyperlink_auditing = False
+
+## Load images automatically in web pages.
+## Type: Bool
+# c.content.images = True
+
+## Show javascript alerts.
+## Type: Bool
+# c.content.javascript.alert = True
+
+## Allow JavaScript to read from or write to the clipboard. With
+## QtWebEngine, writing the clipboard as response to a user interaction
+## is always allowed.
+## Type: Bool
+# c.content.javascript.can_access_clipboard = False
+
+## Allow JavaScript to close tabs.
+## Type: Bool
+# c.content.javascript.can_close_tabs = False
+
+## Allow JavaScript to open new tabs without user interaction.
+## Type: Bool
+# c.content.javascript.can_open_tabs_automatically = False
+
+## Enable JavaScript.
+## Type: Bool
+c.content.javascript.enabled = True
+
+## Log levels to use for JavaScript console logging messages. When a
+## JavaScript message with the level given in the dictionary key is
+## logged, the corresponding dictionary value selects the qutebrowser
+## logger to use. On QtWebKit, the "unknown" setting is always used.
+## Type: Dict
+# c.content.javascript.log = {'unknown': 'debug', 'info': 'debug', 'warning': 'debug', 'error': 'debug'}
+
+## Use the standard JavaScript modal dialog for `alert()` and
+## `confirm()`.
+## Type: Bool
+# c.content.javascript.modal_dialog = False
+
+## Show javascript prompts.
+## Type: Bool
+# c.content.javascript.prompt = True
+
+## Allow locally loaded documents to access other local URLs.
+## Type: Bool
+# c.content.local_content_can_access_file_urls = True
+
+## Allow locally loaded documents to access remote URLs.
+## Type: Bool
+# c.content.local_content_can_access_remote_urls = False
+
+## Enable support for HTML 5 local storage and Web SQL.
+## Type: Bool
+# c.content.local_storage = True
+
+## Allow websites to record audio/video.
+## Type: BoolAsk
+## Valid values:
+## - true
+## - false
+## - ask
+# c.content.media_capture = 'ask'
+
+## Netrc-file for HTTP authentication. If unset, `~/.netrc` is used.
+## Type: File
+# c.content.netrc_file = None
+
+## Allow websites to show notifications.
+## Type: BoolAsk
+## Valid values:
+## - true
+## - false
+## - ask
+# c.content.notifications = 'ask'
+
+## 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.
+## Type: Bool
+# c.content.pdfjs = False
+
+## Enable plugins in Web pages.
+## Type: Bool
+# c.content.plugins = False vgg
+c.content.plugins = True
+
+## Draw the background color and images also when the page is printed.
+## Type: Bool
+# c.content.print_element_backgrounds = True
+
+## Open new windows in private browsing mode which does not record
+## visited pages.
+## Type: Bool
+# c.content.private_browsing = True
+
+## Proxy to use. In addition to the listed values, you can use a
+## `socks://...` or `http://...` URL.
+## Type: Proxy
+## Valid values:
+## - system: Use the system wide proxy.
+## - none: Don't use any proxy
+# c.content.proxy = 'system'
+
+## Send DNS requests over the configured proxy.
+## Type: Bool
+# c.content.proxy_dns_requests = True
+
+## Validate SSL handshakes.
+## Type: BoolAsk
+## Valid values:
+## - true
+## - false
+## - ask
+# c.content.ssl_strict = 'ask'
+
+## List of user stylesheet filenames to use.
+## Type: List of File, or File
+# c.content.user_stylesheets = []
+
+## Enable WebGL.
+## Type: Bool
+# c.content.webgl = True
+
+## Limit fullscreen to the browser window (does not expand to fill the
+## screen).
+## Type: Bool
+# c.content.windowed_fullscreen = False
+
+## Monitor load requests for cross-site scripting attempts. Suspicious
+## scripts will be blocked and reported in the inspector's JavaScript
+## console. Enabling this feature might have an impact on performance.
+## Type: Bool
+# c.content.xss_auditing = False
+
+## Directory to save downloads to. If unset, a sensible OS-specific
+## default is used.
+## Type: Directory
+# c.downloads.location.directory = None
+
+## Prompt the user for the download location. If set to false,
+## `downloads.location.directory` will be used.
+## Type: Bool
+# c.downloads.location.prompt = True
+
+## Remember the last used download directory.
+## Type: Bool
+# c.downloads.location.remember = True
+
+## What to display in the download filename input.
+## Type: String
+## Valid values:
+## - path: Show only the download path.
+## - filename: Show only download filename.
+## - both: Show download path and filename.
+# c.downloads.location.suggestion = 'path'
+
+## Default program used to open downloads. If null, the default internal
+## handler is used. Any `{}` in the string will be expanded to the
+## filename, else the filename will be appended.
+## Type: String
+# c.downloads.open_dispatcher = None
+
+## Where to show the downloaded files.
+## Type: VerticalPosition
+## Valid values:
+## - top
+## - bottom
+# c.downloads.position = 'top'
+
+## Duration (in milliseconds) to wait before removing finished downloads.
+## If set to -1, downloads are never removed.
+## Type: Int
+# c.downloads.remove_finished = -1
+
+## Editor (and arguments) to use for the `open-editor` command. The
+## following placeholders are defined: * `{file}`: Filename of the file
+## to be edited. * `{line}`: Line in which the caret is found in the
+## text. * `{column}`: Column in which the caret is found in the text. *
+## `{line0}`: Same as `{line}`, but starting from index 0. * `{column0}`:
+## Same as `{column}`, but starting from index 0.
+## Type: ShellCommand
+# c.editor.command = ['gvim', '-f', '{file}', '-c', 'normal {line}G{column0}l']
+c.editor.command = ["st", "-t", "qutebrowserSratchpad", "-g", "86x24+40+60", "-e", "nvim", "-f", "{}"]
+
+## Encoding to use for the editor.
+## Type: Encoding
+# c.editor.encoding = 'utf-8'
+
+## Font used in the completion categories.
+## Type: Font
+# c.fonts.completion.category = 'bold 10pt monospace'
+
+## Font used in the completion widget.
+## Type: Font
+# c.fonts.completion.entry = '10pt monospace'
+
+## Font used for the debugging console.
+## Type: QtFont
+# c.fonts.debug_console = '10pt monospace'
+
+## Font used for the downloadbar.
+## Type: Font
+# c.fonts.downloads = '10pt monospace'
+
+## Font used for the hints.
+## Type: Font
+c.fonts.hints = 'bold 8pt monospace'
+
+## Font used in the keyhint widget.
+## Type: Font
+# c.fonts.keyhint = '10pt monospace'
+
+## Font used for error messages.
+## Type: Font
+# c.fonts.messages.error = '10pt monospace'
+
+## Font used for info messages.
+## Type: Font
+# c.fonts.messages.info = '10pt monospace'
+
+## Font used for warning messages.
+## Type: Font
+# c.fonts.messages.warning = '10pt monospace'
+
+## Default monospace fonts. Whenever "monospace" is used in a font
+## setting, it's replaced with the fonts listed here.
+## Type: Font
+# c.fonts.monospace = '"xos4 Terminus", Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal'
+
+## Font used for prompts.
+## Type: Font
+# c.fonts.prompts = '10pt sans-serif'
+
+## Font used in the statusbar.
+## Type: Font
+# c.fonts.statusbar = '10pt monospace'
+
+## Font used in the tab bar.
+## Type: QtFont
+# c.fonts.tabs = '10pt monospace'
+
+## Font family for cursive fonts.
+## Type: FontFamily
+# c.fonts.web.family.cursive = ''
+
+## Font family for fantasy fonts.
+## Type: FontFamily
+# c.fonts.web.family.fantasy = ''
+
+## Font family for fixed fonts.
+## Type: FontFamily
+# c.fonts.web.family.fixed = ''
+
+## Font family for sans-serif fonts.
+## Type: FontFamily
+# c.fonts.web.family.sans_serif = ''
+
+## Font family for serif fonts.
+## Type: FontFamily
+# c.fonts.web.family.serif = ''
+
+## Font family for standard fonts.
+## Type: FontFamily
+# c.fonts.web.family.standard = ''
+
+## Default font size (in pixels) for regular text.
+## Type: Int
+# c.fonts.web.size.default = 16
+
+## Default font size (in pixels) for fixed-pitch text.
+## Type: Int
+# c.fonts.web.size.default_fixed = 13
+
+## Hard minimum font size (in pixels).
+## Type: Int
+# c.fonts.web.size.minimum = 0
+
+## Minimum logical font size (in pixels) that is applied when zooming
+## out.
+## Type: Int
+# c.fonts.web.size.minimum_logical = 6
+
+## When a hint can be automatically followed without pressing Enter.
+## Type: String
+## Valid values:
+## - always: Auto-follow whenever there is only a single hint on a page.
+## - unique-match: Auto-follow whenever there is a unique non-empty match in either the hint string (word mode) or filter (number mode).
+## - full-match: Follow the hint when the user typed the whole hint (letter, word or number mode) or the element's text (only in number mode).
+## - never: The user will always need to press Enter to follow a hint.
+# c.hints.auto_follow = 'unique-match'
+
+## Duration (in milliseconds) to ignore normal-mode key bindings after a
+## successful auto-follow.
+## Type: Int
+# c.hints.auto_follow_timeout = 0
+
+## CSS border value for hints.
+## Type: String
+# c.hints.border = '1px solid #E3BE23'
+
+## Characters used for hint strings.
+## Type: UniqueCharString
+# c.hints.chars = 'asdfghjkl'
+
+## Dictionary file to be used by the word hints.
+## Type: File
+# c.hints.dictionary = '/usr/share/dict/words'
+
+## Which implementation to use to find elements to hint.
+## Type: String
+## Valid values:
+## - javascript: Better but slower
+## - python: Slightly worse but faster
+# c.hints.find_implementation = 'python'
+
+## Hide unmatched hints in rapid mode.
+## Type: Bool
+# c.hints.hide_unmatched_rapid_hints = True
+
+## Minimum number of characters used for hint strings.
+## Type: Int
+# c.hints.min_chars = 1
+
+## Mode to use for hints.
+## Type: String
+## Valid values:
+## - number: Use numeric hints. (In this mode you can also type letters from the hinted element to filter and reduce the number of elements that are hinted.)
+## - letter: Use the characters in the `hints.chars` setting.
+## - word: Use hints words based on the html elements and the extra words.
+c.hints.mode = 'number'
+
+## Comma-separated list of regular expressions to use for 'next' links.
+## Type: List of Regex
+# c.hints.next_regexes = ['\\bnext\\b', '\\bmore\\b', '\\bnewer\\b', '\\b[>→≫]\\b', '\\b(>>|»)\\b', '\\bcontinue\\b']
+
+## Comma-separated list of regular expressions to use for 'prev' links.
+## Type: List of Regex
+# c.hints.prev_regexes = ['\\bprev(ious)?\\b', '\\bback\\b', '\\bolder\\b', '\\b[<←≪]\\b', '\\b(<<|«)\\b']
+
+## Scatter hint key chains (like Vimium) or not (like dwb). Ignored for
+## number hints.
+## Type: Bool
+# c.hints.scatter = True
+
+## Make characters in hint strings uppercase.
+## Type: Bool
+# c.hints.uppercase = False
+
+## Maximum time (in minutes) between two history items for them to be
+## considered being from the same browsing session. Items with less time
+## between them are grouped when being displayed in `:history`. Use -1 to
+## disable separation.
+## Type: Int
+# c.history_gap_interval = 30
+
+## Which unbound keys to forward to the webview in normal mode.
+## Type: String
+## Valid values:
+## - all: Forward all unbound keys.
+## - auto: Forward unbound non-alphanumeric keys.
+## - none: Don't forward any keys.
+# c.input.forward_unbound_keys = 'auto'
+
+## Enter insert mode if an editable element is clicked.
+## Type: Bool
+# c.input.insert_mode.auto_enter = True
+
+## Leave insert mode if a non-editable element is clicked.
+## Type: Bool
+# c.input.insert_mode.auto_leave = True
+
+## Automatically enter insert mode if an editable element is focused
+## after loading the page.
+## Type: Bool
+# c.input.insert_mode.auto_load = False
+
+## Switch to insert mode when clicking flash and other plugins.
+## Type: Bool
+# c.input.insert_mode.plugins = False
+
+## Include hyperlinks in the keyboard focus chain when tabbing.
+## Type: Bool
+# c.input.links_included_in_focus_chain = True
+
+## Timeout (in milliseconds) for partially typed key bindings. If the
+## current input forms only partial matches, the keystring will be
+## cleared after this time.
+## Type: Int
+# c.input.partial_timeout = 5000
+
+## Enable Opera-like mouse rocker gestures. This disables the context
+## menu.
+## Type: Bool
+# c.input.rocker_gestures = False
+
+## 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.
+## Type: Bool
+# c.input.spatial_navigation = False
+
+## Keychains that shouldn't be shown in the keyhint dialog. Globs are
+## supported, so `;*` will blacklist all keychains starting with `;`. Use
+## `*` to disable keyhints.
+## Type: List of String
+# c.keyhint.blacklist = []
+
+## Time (in milliseconds) from pressing a key to seeing the keyhint
+## dialog.
+## Type: Int
+# c.keyhint.delay = 500
+
+## Rounding radius (in pixels) for the edges of the keyhint dialog.
+## Type: Int
+# c.keyhint.radius = 6
+
+## Duration (in milliseconds) to show messages in the statusbar for. Set
+## to 0 to never clear messages.
+## Type: Int
+# c.messages.timeout = 2000
+
+## How to open links in an existing instance if a new one is launched.
+## This happens when e.g. opening a link from a terminal. See
+## `new_instance_open_target_window` to customize in which window the
+## link is opened in.
+## Type: String
+## Valid values:
+## - tab: Open a new tab in the existing window and activate the window.
+## - tab-bg: Open a new background tab in the existing window and activate the window.
+## - tab-silent: Open a new tab in the existing window without activating the window.
+## - tab-bg-silent: Open a new background tab in the existing window without activating the window.
+## - window: Open in a new window.
+# c.new_instance_open_target = 'tab'
+
+## Which window to choose when opening links as new tabs. When
+## `new_instance_open_target` is not set to `window`, this is ignored.
+## Type: String
+## Valid values:
+## - first-opened: Open new tabs in the first (oldest) opened window.
+## - last-opened: Open new tabs in the last (newest) opened window.
+## - last-focused: Open new tabs in the most recently focused window.
+## - last-visible: Open new tabs in the most recently visible window.
+# c.new_instance_open_target_window = 'last-focused'
+
+## Show a filebrowser in upload/download prompts.
+## Type: Bool
+# c.prompt.filebrowser = True
+
+## Rounding radius (in pixels) for the edges of prompts.
+## Type: Int
+# c.prompt.radius = 8
+
+## Additional arguments to pass to Qt, without leading `--`. With
+## QtWebEngine, some Chromium arguments (see
+## https://peter.sh/experiments/chromium-command-line-switches/ for a
+## list) will work.
+## Type: List of String
+# c.qt.args = []
+
+## Force a Qt platform to use. This sets the `QT_QPA_PLATFORM`
+## environment variable and is useful to force using the XCB plugin when
+## running QtWebEngine on Wayland.
+## Type: String
+# c.qt.force_platform = None
+
+## Force software rendering for QtWebEngine. This is needed for
+## QtWebEngine to work with Nouveau drivers.
+## Type: Bool
+# c.qt.force_software_rendering = False
+
+## Turn on Qt HighDPI scaling. This is equivalent to setting
+## QT_AUTO_SCREEN_SCALE_FACTOR=1 in the environment. It's off by default
+## as it can cause issues with some bitmap fonts. As an alternative to
+## this, it's possible to set font sizes and the `zoom.default` setting.
+## Type: Bool
+# c.qt.highdpi = False
+
+## Show a scrollbar.
+## Type: Bool
+# c.scrolling.bar = False
+
+## Enable smooth scrolling for web pages. Note smooth scrolling does not
+## work with the `:scroll-px` command.
+## Type: Bool
+# c.scrolling.smooth = False
+
+## When to find text on a page case-insensitively.
+## Type: String
+## Valid values:
+## - always: Search case-insensitively.
+## - never: Search case-sensitively.
+## - smart: Search case-sensitively if there are capital characters.
+# c.search.ignore_case = 'smart'
+
+## Find text on a page incrementally, renewing the search for each typed
+## character.
+## Type: Bool
+# c.search.incremental = True
+
+## Name of the session to save by default. If this is set to null, the
+## session which was last loaded is saved.
+## Type: SessionName
+# c.session.default_name = None
+
+## Load a restored tab as soon as it takes focus.
+## Type: Bool
+# c.session.lazy_restore = False
+
+## Languages to use for spell checking. You can check for available
+## languages and install dictionaries using scripts/dictcli.py. Run the
+## script with -h/--help for instructions.
+## Type: List of String
+## Valid values:
+## - af-ZA: Afrikaans (South Africa)
+## - bg-BG: Bulgarian (Bulgaria)
+## - ca-ES: Catalan (Spain)
+## - cs-CZ: Czech (Czech Republic)
+## - da-DK: Danish (Denmark)
+## - de-DE: German (Germany)
+## - el-GR: Greek (Greece)
+## - en-AU: English (Australia)
+## - en-CA: English (Canada)
+## - en-GB: English (United Kingdom)
+## - en-US: English (United States)
+## - es-ES: Spanish (Spain)
+## - et-EE: Estonian (Estonia)
+## - fa-IR: Farsi (Iran)
+## - fo-FO: Faroese (Faroe Islands)
+## - fr-FR: French (France)
+## - he-IL: Hebrew (Israel)
+## - hi-IN: Hindi (India)
+## - hr-HR: Croatian (Croatia)
+## - hu-HU: Hungarian (Hungary)
+## - id-ID: Indonesian (Indonesia)
+## - it-IT: Italian (Italy)
+## - ko: Korean
+## - lt-LT: Lithuanian (Lithuania)
+## - lv-LV: Latvian (Latvia)
+## - nb-NO: Norwegian (Norway)
+## - nl-NL: Dutch (Netherlands)
+## - pl-PL: Polish (Poland)
+## - pt-BR: Portuguese (Brazil)
+## - pt-PT: Portuguese (Portugal)
+## - ro-RO: Romanian (Romania)
+## - ru-RU: Russian (Russia)
+## - sh: Serbo-Croatian
+## - sk-SK: Slovak (Slovakia)
+## - sl-SI: Slovenian (Slovenia)
+## - sq: Albanian
+## - sr: Serbian
+## - sv-SE: Swedish (Sweden)
+## - ta-IN: Tamil (India)
+## - tg-TG: Tajik (Tajikistan)
+## - tr-TR: Turkish (Turkey)
+## - uk-UA: Ukrainian (Ukraine)
+## - vi-VN: Vietnamese (Viet Nam)
+# c.spellcheck.languages = []
+
+## Hide the statusbar unless a message is shown.
+## Type: Bool
+# c.statusbar.hide = False
+
+## Padding (in pixels) for the statusbar.
+## Type: Padding
+# c.statusbar.padding = {'top': 1, 'bottom': 1, 'left': 0, 'right': 0}
+
+## Position of the status bar.
+## Type: VerticalPosition
+## Valid values:
+## - top
+## - bottom
+# c.statusbar.position = 'bottom'
+
+## List of widgets displayed in the statusbar.
+## Type: List of String
+## Valid values:
+## - url: Current page URL.
+## - scroll: Percentage of the current page position like `10%`.
+## - scroll_raw: Raw percentage of the current page position like `10`.
+## - history: Display an arrow when possible to go back/forward in history.
+## - tabs: Current active tab, e.g. `2`.
+## - keypress: Display pressed keys when composing a vi command.
+## - progress: Progress bar for the current page loading.
+# c.statusbar.widgets = ['keypress', 'url', 'scroll', 'history', 'tabs', 'progress']
+
+## Open new tabs (middleclick/ctrl+click) in the background.
+## Type: Bool
+# c.tabs.background = False
+
+## Mouse button with which to close tabs.
+## Type: String
+## Valid values:
+## - right: Close tabs on right-click.
+## - middle: Close tabs on middle-click.
+## - none: Don't close tabs using the mouse.
+# c.tabs.close_mouse_button = 'middle'
+
+## How to behave when the close mouse button is pressed on the tab bar.
+## Type: String
+## Valid values:
+## - new-tab: Open a new tab.
+## - close-current: Close the current tab.
+## - close-last: Close the last tab.
+## - ignore: Don't do anything.
+# c.tabs.close_mouse_button_on_bar = 'new-tab'
+
+## Scaling factor for favicons in the tab bar. The tab size is unchanged,
+## so big favicons also require extra `tabs.padding`.
+## Type: Float
+# c.tabs.favicons.scale = 1.0
+
+## Show favicons in the tab bar.
+## Type: Bool
+# c.tabs.favicons.show = True
+
+## Padding (in pixels) for tab indicators.
+## Type: Padding
+# c.tabs.indicator.padding = {'top': 2, 'bottom': 2, 'left': 0, 'right': 4}
+
+## Width (in pixels) of the progress indicator (0 to disable).
+## Type: Int
+# c.tabs.indicator.width = 3
+
+## How to behave when the last tab is closed.
+## Type: String
+## Valid values:
+## - ignore: Don't do anything.
+## - blank: Load a blank page.
+## - startpage: Load the start page.
+## - default-page: Load the default page.
+## - close: Close the window.
+# c.tabs.last_close = 'ignore'
+
+## When switching tabs, what input mode is applied.
+## Type: String
+## Valid values:
+## - persist: Retain the current mode.
+## - restore: Restore previously saved mode.
+## - normal: Always revert to normal mode.
+# c.tabs.mode_on_change = 'normal'
+
+## Switch between tabs using the mouse wheel.
+## Type: Bool
+# c.tabs.mousewheel_switching = True
+
+## Position of new tabs opened from another tab.
+## Type: NewTabPosition
+## Valid values:
+## - prev: Before the current tab.
+## - next: After the current tab.
+## - first: At the beginning.
+## - last: At the end.
+# c.tabs.new_position.related = 'next'
+
+## Position of new tabs which aren't opened from another tab.
+## Type: NewTabPosition
+## Valid values:
+## - prev: Before the current tab.
+## - next: After the current tab.
+## - first: At the beginning.
+## - last: At the end.
+# c.tabs.new_position.unrelated = 'last'
+
+## Padding (in pixels) around text for tabs.
+## Type: Padding
+# c.tabs.padding = {'top': 0, 'bottom': 0, 'left': 5, 'right': 5}
+
+## Shrink pinned tabs down to their contents.
+## Type: Bool
+# c.tabs.pinned.shrink = True
+
+## Position of the tab bar.
+## Type: Position
+## Valid values:
+## - top
+## - bottom
+## - left
+## - right
+# c.tabs.position = 'top'
+
+## Which tab to select when the focused tab is removed.
+## Type: SelectOnRemove
+## Valid values:
+## - prev: Select the tab which came before the closed one (left in horizontal, above in vertical).
+## - next: Select the tab which came after the closed one (right in horizontal, below in vertical).
+## - last-used: Select the previously selected tab.
+# c.tabs.select_on_remove = 'next'
+
+## When to show the tab bar.
+## Type: String
+## Valid values:
+## - always: Always show the tab bar.
+## - never: Always hide the tab bar.
+## - multiple: Hide the tab bar if only one tab is open.
+## - switching: Show the tab bar when switching tabs.
+# c.tabs.show = 'always'
+
+## Duration (in milliseconds) to show the tab bar before hiding it when
+## tabs.show is set to 'switching'.
+## Type: Int
+# c.tabs.show_switching_delay = 800
+
+## Open a new window for every tab.
+## Type: Bool
+# c.tabs.tabs_are_windows = False
+
+## Alignment of the text inside of tabs.
+## Type: TextAlignment
+## Valid values:
+## - left
+## - right
+## - center
+# c.tabs.title.alignment = 'left'
+
+## Format to use for the tab title. The following placeholders are
+## defined: * `{perc}`: Percentage as a string like `[10%]`. *
+## `{perc_raw}`: Raw percentage, e.g. `10`. * `{title}`: Title of the
+## current web page. * `{title_sep}`: The string ` - ` if a title is set,
+## empty otherwise. * `{index}`: Index of this tab. * `{id}`: Internal
+## tab ID of this tab. * `{scroll_pos}`: Page scroll position. *
+## `{host}`: Host of the current web page. * `{backend}`: Either
+## ''webkit'' or ''webengine'' * `{private}`: Indicates when private mode
+## is enabled. * `{current_url}`: URL of the current web page. *
+## `{protocol}`: Protocol (http/https/...) of the current web page.
+## Type: FormatString
+# c.tabs.title.format = '{index}: {title}'
+
+## Format to use for the tab title for pinned tabs. The same placeholders
+## like for `tabs.title.format` are defined.
+## Type: FormatString
+# c.tabs.title.format_pinned = '{index}'
+
+## Width (in pixels or as percentage of the window) of the tab bar if
+## it's vertical.
+## Type: PercOrInt
+# c.tabs.width = '20%'
+
+## Wrap when changing tabs.
+## Type: Bool
+# c.tabs.wrap = True
+
+## What search to start when something else than a URL is entered.
+## Type: String
+## Valid values:
+## - naive: Use simple/naive check.
+## - dns: Use DNS requests (might be slow!).
+## - never: Never search automatically.
+# c.url.auto_search = 'naive'
+
+## Page to open if :open -t/-b/-w is used without URL. Use `about:blank`
+## for a blank page.
+## Type: FuzzyUrl
+# c.url.default_page = 'https://start.duckduckgo.com/'
+
+## URL segments where `:navigate increment/decrement` will search for a
+## number.
+## Type: FlagList
+## Valid values:
+## - host
+## - port
+## - path
+## - query
+## - anchor
+# c.url.incdec_segments = ['path', 'query']
+
+## Search engines which can be used via the address bar. Maps a search
+## engine name (such as `DEFAULT`, or `ddg`) to a URL with a `{}`
+## placeholder. The placeholder will be replaced by the search term, use
+## `{{` and `}}` for literal `{`/`}` signs. The search engine named
+## `DEFAULT` is used when `url.auto_search` is turned on and something
+## else than a URL was entered to be opened. Other search engines can be
+## used by prepending the search engine name to the search term, e.g.
+## `:open google qutebrowser`.
+## Type: Dict
+
+c.url.searchengines = { 'DEFAULT':'https://duckduckgo.com/?q={}',
+ 'aio':'https://www.aiosearch.com/search?q={}',
+ 'aw':'https://wiki.archlinux.org/?q={}',
+ 'g':'https://www.google.com/?q={}',
+ 'git':'https://github.com/search?q={}',
+ 'imdb':'https://www.imdb.com/find?q={}',
+ 'red':'https://www.reddit.com/r/{}',
+ 'ub':'https://www.urbandictionary.com/define.php?term={}',
+ 'wiki':'https://en.wikipedia.org/wiki/?q={}',
+ 'yfs':'https://www.yifysubtitles.com/search?q={}',
+ 'yt':'https://www.youtube.com/results?search_query={}'}
+
+## Page(s) to open at the start.
+## Type: List of FuzzyUrl, or FuzzyUrl
+# c.url.start_pages = ['https://start.duckduckgo.com']
+
+## URL parameters to strip with `:yank url`.
+## Type: List of String
+# c.url.yank_ignored_parameters = ['ref', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']
+
+## Hide the window decoration when using wayland.
+## Type: Bool
+# c.window.hide_wayland_decoration = False
+
+## Format to use for the window title. The same placeholders like for
+## `tabs.title.format` are defined.
+## Type: FormatString
+# c.window.title_format = '{perc}{title}{title_sep}qutebrowser'
+
+## Default zoom level.
+## Type: Perc
+# c.zoom.default = '100%'
+
+## Available zoom levels.
+## Type: List of Perc
+# c.zoom.levels = ['25%', '33%', '50%', '67%', '75%', '90%', '100%', '110%', '125%', '150%', '175%', '200%', '250%', '300%', '400%', '500%']
+
+## Number of zoom increments to divide the mouse wheel movements to.
+## Type: Int
+# c.zoom.mouse_divider = 512
+
+## Apply the zoom factor on a frame only to the text or to all content.
+## Type: Bool
+# c.zoom.text_only = False
+
+## Bindings for normal mode
+config.bind(',c', 'hint links spawn mpv --ytdl --ytdl-raw-options=username="v.graffagnino@btinternet.com",password="N9\Ch0n\H@n9" --cookies-file=~/.local/share/qutebrowser/webengine/Cookies {hint-url} --load-unsafe-playlists')
+config.bind(',m', 'hint links spawn mpv --ytdl {hint-url} --load-unsafe-playlists')
+#config.bind('m', 'hint links spawn mpv --ytdl -slang=en {hint-url} --load-unsafe-playlists')
+config.bind('m', '/usr/bin/youtube-dl -x --restrict-filenames --audio-format="mp3" --audio-quality="128k" -o "%(artist)s-%(title)s.%(ext)s" {hint-url}')
+config.bind(',M', 'hint links spawn umpv {hint-url}')
+config.bind(',t', 'hint links spawn transmission-remote --add {hint-url}')
+#config.bind(',y', 'hint links spawn /usr/local/bin/st -e tsp youtube-dl -b -o %(channel)s/%(title)s.%(ext)s {hint-url}')
+#config.bind(',y', 'hint links spawn /usr/local/bin/st -e /usr/bin/tsp youtube-dl -b {hint-url}')
+config.bind(',y', 'hint links spawn /usr/local/bin/st -e tsp youtube-dl --add-metadata -ic -f best {hint-url}')
+config.bind(',Y', 'hint links spawn /usr/local/bin/st -e mpsyt dlurl {hint-url}')
+config.bind(',q', 'hint links spawn echo "mpsyt dlurl {hint-url}" >> ~/ytdl.list')
+config.bind(',p', 'spawn --userscript qute-pass --dmenu-invocation dmenu')
+config.bind('xb', 'config-cycle statusbar.hide')
+config.bind('xt', 'config-cycle tabs.show always switching')
+config.bind('xx', 'config-cycle statusbar.hide ;; config-cycle tabs.show always switching')
+config.bind('xp', 'set-cmd-text :set content.proxy socks://localhost:8765')
+config.bind('Xp', 'set-cmd-text :config-unset content.proxy')
+#config.bind('ua', 'set-cmd-text :set -u https://accounts.google.com/* content.headers.user_agent "Mozilla/5.0 (X11; Linux x86_64; rv:57.0) Gecko/20100101 Firefox/57.0")
+
+# config.bind("'", 'enter-mode jump_mark')
+# config.bind('+', 'zoom-in')
+# config.bind('-', 'zoom-out')
+# config.bind('.', 'repeat-command')
+# config.bind('/', 'set-cmd-text /')
+# config.bind(':', 'set-cmd-text :')
+# config.bind(';I', 'hint images tab')
+# config.bind(';O', 'hint links fill :open -t -r {hint-url}')
+# config.bind(';R', 'hint --rapid links window')
+# config.bind(';Y', 'hint links yank-primary')
+# config.bind(';b', 'hint all tab-bg')
+# config.bind(';d', 'hint links download')
+# config.bind(';f', 'hint all tab-fg')
+# config.bind(';h', 'hint all hover')
+# config.bind(';i', 'hint images')
+# config.bind(';o', 'hint links fill :open {hint-url}')
+# config.bind(';r', 'hint --rapid links tab-bg')
+# config.bind(';t', 'hint inputs')
+# config.bind(';y', 'hint links yank')
+# config.bind('<Alt-1>', 'tab-focus 1')
+# config.bind('<Alt-2>', 'tab-focus 2')
+# config.bind('<Alt-3>', 'tab-focus 3')
+# config.bind('<Alt-4>', 'tab-focus 4')
+# config.bind('<Alt-5>', 'tab-focus 5')
+# config.bind('<Alt-6>', 'tab-focus 6')
+# config.bind('<Alt-7>', 'tab-focus 7')
+# config.bind('<Alt-8>', 'tab-focus 8')
+# config.bind('<Alt-9>', 'tab-focus -1')
+# config.bind('<Ctrl-A>', 'navigate increment')
+# config.bind('<Ctrl-Alt-p>', 'print')
+# config.bind('<Ctrl-B>', 'scroll-page 0 -1')
+# config.bind('<Ctrl-D>', 'scroll-page 0 0.5')
+# config.bind('<Ctrl-F5>', 'reload -f')
+# config.bind('<Ctrl-F>', 'scroll-page 0 1')
+# config.bind('<Ctrl-N>', 'open -w')
+# config.bind('<Ctrl-PgDown>', 'tab-next')
+# config.bind('<Ctrl-PgUp>', 'tab-prev')
+# config.bind('<Ctrl-Q>', 'quit')
+# config.bind('<Ctrl-Return>', 'follow-selected -t')
+# config.bind('<Ctrl-Shift-N>', 'open -p')
+# config.bind('<Ctrl-Shift-T>', 'undo')
+# config.bind('<Ctrl-Shift-W>', 'close')
+# config.bind('<Ctrl-T>', 'open -t')
+# config.bind('<Ctrl-Tab>', 'tab-focus last')
+# config.bind('<Ctrl-U>', 'scroll-page 0 -0.5')
+# config.bind('<Ctrl-V>', 'enter-mode passthrough')
+# config.bind('<Ctrl-W>', 'tab-close')
+# config.bind('<Ctrl-X>', 'navigate decrement')
+# config.bind('<Ctrl-^>', 'tab-focus last')
+# config.bind('<Ctrl-h>', 'home')
+# config.bind('<Ctrl-p>', 'tab-pin')
+# config.bind('<Ctrl-s>', 'stop')
+# config.bind('<Escape>', 'clear-keychain ;; search ;; fullscreen --leave')
+# config.bind('<F11>', 'fullscreen')
+# config.bind('<F5>', 'reload')
+# config.bind('<Return>', 'follow-selected')
+# config.bind('<back>', 'back')
+# config.bind('<forward>', 'forward')
+# config.bind('=', 'zoom')
+# config.bind('?', 'set-cmd-text ?')
+# config.bind('@', 'run-macro')
+# config.bind('B', 'set-cmd-text -s :quickmark-load -t')
+# config.bind('D', 'tab-close -o')
+# config.bind('F', 'hint all tab')
+# config.bind('G', 'scroll-to-perc')
+# config.bind('H', 'back')
+config.bind('J', 'tab-prev')
+config.bind('K', 'tab-next')
+# config.bind('L', 'forward')
+# config.bind('M', 'bookmark-add')
+# config.bind('N', 'search-prev')
+# config.bind('O', 'set-cmd-text -s :open -t')
+# config.bind('PP', 'open -t -- {primary}')
+# config.bind('Pp', 'open -t -- {clipboard}')
+# config.bind('R', 'reload -f')
+# config.bind('Sb', 'open qute://bookmarks#bookmarks')
+# config.bind('Sh', 'open qute://history')
+# config.bind('Sq', 'open qute://bookmarks')
+# config.bind('Ss', 'open qute://settings')
+# config.bind('T', 'tab-focus')
+# config.bind('ZQ', 'quit')
+# config.bind('ZZ', 'quit --save')
+# config.bind('[[', 'navigate prev')
+# config.bind(']]', 'navigate next')
+# config.bind('`', 'enter-mode set_mark')
+# config.bind('ad', 'download-cancel')
+# config.bind('b', 'set-cmd-text -s :quickmark-load')
+# config.bind('cd', 'download-clear')
+# config.bind('co', 'tab-only')
+# config.bind('d', 'tab-close')
+# config.bind('f', 'hint')
+# config.bind('g$', 'tab-focus -1')
+# config.bind('g0', 'tab-focus 1')
+# config.bind('gB', 'set-cmd-text -s :bookmark-load -t')
+# config.bind('gC', 'tab-clone')
+# config.bind('gO', 'set-cmd-text :open -t -r {url:pretty}')
+# config.bind('gU', 'navigate up -t')
+# config.bind('g^', 'tab-focus 1')
+# config.bind('ga', 'open -t')
+# config.bind('gb', 'set-cmd-text -s :bookmark-load')
+config.bind('gd', 'download')
+config.bind('gf', 'view-source')
+# config.bind('gg', 'scroll-to-perc 0')
+# config.bind('gl', 'tab-move -')
+# config.bind('gm', 'tab-move')
+# config.bind('go', 'set-cmd-text :open {url:pretty}')
+# config.bind('gr', 'tab-move +')
+# config.bind('gt', 'set-cmd-text -s :buffer')
+# config.bind('gu', 'navigate up')
+# config.bind('h', 'scroll left')
+# config.bind('i', 'enter-mode insert')
+# config.bind('j', 'scroll down')
+# config.bind('k', 'scroll up')
+# config.bind('l', 'scroll right')
+# config.bind('m', 'quickmark-save')
+# config.bind('n', 'search-next')
+# config.bind('o', 'set-cmd-text -s :open')
+# config.bind('pP', 'open -- {primary}')
+# config.bind('pp', 'open -- {clipboard}')
+# config.bind('q', 'record-macro')
+# config.bind('r', 'reload')
+# config.bind('sf', 'save')
+# config.bind('sk', 'set-cmd-text -s :bind')
+# config.bind('sl', 'set-cmd-text -s :set -t')
+# config.bind('ss', 'set-cmd-text -s :set')
+# config.bind('tPH', 'config-cycle -p -u *://*.{url:host}/* content.plugins ;; reload')
+# config.bind('tPh', 'config-cycle -p -u *://{url:host}/* content.plugins ;; reload')
+# config.bind('tPu', 'config-cycle -p -u {url} content.plugins ;; reload')
+config.bind('tSH', 'config-cycle -p -u *://*.{url:host}/* content.javascript.enabled ;; reload')
+config.bind('tSh', 'config-cycle -p -u *://{url:host}/* content.javascript.enabled ;; reload')
+config.bind('tSu', 'config-cycle -p -u {url} content.javascript.enabled ;; reload')
+# config.bind('th', 'back -t')
+# config.bind('tl', 'forward -t')
+# config.bind('tpH', 'config-cycle -p -t -u *://*.{url:host}/* content.plugins ;; reload')
+# config.bind('tph', 'config-cycle -p -t -u *://{url:host}/* content.plugins ;; reload')
+# config.bind('tpu', 'config-cycle -p -t -u {url} content.plugins ;; reload')
+# config.bind('tsH', 'config-cycle -p -t -u *://*.{url:host}/* content.javascript.enabled ;; reload')
+# config.bind('tsh', 'config-cycle -p -t -u *://{url:host}/* content.javascript.enabled ;; reload')
+# config.bind('tsu', 'config-cycle -p -t -u {url} content.javascript.enabled ;; reload')
+# config.bind('u', 'undo')
+# config.bind('v', 'enter-mode caret')
+# config.bind('wB', 'set-cmd-text -s :bookmark-load -w')
+# config.bind('wO', 'set-cmd-text :open -w {url:pretty}')
+# config.bind('wP', 'open -w -- {primary}')
+# config.bind('wb', 'set-cmd-text -s :quickmark-load -w')
+# config.bind('wf', 'hint all window')
+# config.bind('wh', 'back -w')
+# config.bind('wi', 'inspector')
+# config.bind('wl', 'forward -w')
+# config.bind('wo', 'set-cmd-text -s :open -w')
+# config.bind('wp', 'open -w -- {clipboard}')
+# config.bind('xO', 'set-cmd-text :open -b -r {url:pretty}')
+# config.bind('xo', 'set-cmd-text -s :open -b')
+# config.bind('yD', 'yank domain -s')
+# config.bind('yP', 'yank pretty-url -s')
+# config.bind('yT', 'yank title -s')
+# config.bind('yY', 'yank -s')
+# config.bind('yd', 'yank domain')
+# config.bind('yp', 'yank pretty-url')
+# config.bind('yt', 'yank title')
+# config.bind('yy', 'yank')
+# config.bind('{{', 'navigate prev -t')
+# config.bind('}}', 'navigate next -t')
+
+## Bindings for caret mode
+# config.bind('$', 'move-to-end-of-line', mode='caret')
+# config.bind('0', 'move-to-start-of-line', mode='caret')
+# config.bind('<Ctrl-Space>', 'drop-selection', mode='caret')
+# config.bind('<Escape>', 'leave-mode', mode='caret')
+# config.bind('<Return>', 'yank selection', mode='caret')
+# config.bind('<Space>', 'toggle-selection', mode='caret')
+# config.bind('G', 'move-to-end-of-document', mode='caret')
+# config.bind('H', 'scroll left', mode='caret')
+# config.bind('J', 'scroll down', mode='caret')
+# config.bind('K', 'scroll up', mode='caret')
+# config.bind('L', 'scroll right', mode='caret')
+# config.bind('Y', 'yank selection -s', mode='caret')
+# config.bind('[', 'move-to-start-of-prev-block', mode='caret')
+# config.bind(']', 'move-to-start-of-next-block', mode='caret')
+# config.bind('b', 'move-to-prev-word', mode='caret')
+# config.bind('c', 'enter-mode normal', mode='caret')
+# config.bind('e', 'move-to-end-of-word', mode='caret')
+# config.bind('gg', 'move-to-start-of-document', mode='caret')
+# config.bind('h', 'move-to-prev-char', mode='caret')
+# config.bind('j', 'move-to-next-line', mode='caret')
+# config.bind('k', 'move-to-prev-line', mode='caret')
+# config.bind('l', 'move-to-next-char', mode='caret')
+# config.bind('v', 'toggle-selection', mode='caret')
+# config.bind('w', 'move-to-next-word', mode='caret')
+# config.bind('y', 'yank selection', mode='caret')
+# config.bind('{', 'move-to-end-of-prev-block', mode='caret')
+# config.bind('}', 'move-to-end-of-next-block', mode='caret')
+
+## Bindings for command mode
+# config.bind('<Alt-B>', 'rl-backward-word', mode='command')
+# config.bind('<Alt-Backspace>', 'rl-backward-kill-word', mode='command')
+# config.bind('<Alt-D>', 'rl-kill-word', mode='command')
+# config.bind('<Alt-F>', 'rl-forward-word', mode='command')
+# config.bind('<Ctrl-?>', 'rl-delete-char', mode='command')
+# config.bind('<Ctrl-A>', 'rl-beginning-of-line', mode='command')
+# config.bind('<Ctrl-B>', 'rl-backward-char', mode='command')
+# config.bind('<Ctrl-C>', 'completion-item-yank', mode='command')
+# config.bind('<Ctrl-D>', 'completion-item-del', mode='command')
+# config.bind('<Ctrl-E>', 'rl-end-of-line', mode='command')
+# config.bind('<Ctrl-F>', 'rl-forward-char', mode='command')
+# config.bind('<Ctrl-H>', 'rl-backward-delete-char', mode='command')
+# config.bind('<Ctrl-K>', 'rl-kill-line', mode='command')
+# config.bind('<Ctrl-N>', 'command-history-next', mode='command')
+# config.bind('<Ctrl-P>', 'command-history-prev', mode='command')
+# config.bind('<Ctrl-Return>', 'command-accept --rapid', mode='command')
+# config.bind('<Ctrl-Shift-C>', 'completion-item-yank --sel', mode='command')
+# config.bind('<Ctrl-Shift-Tab>', 'completion-item-focus prev-category', mode='command')
+# config.bind('<Ctrl-Tab>', 'completion-item-focus next-category', mode='command')
+# config.bind('<Ctrl-U>', 'rl-unix-line-discard', mode='command')
+# config.bind('<Ctrl-W>', 'rl-unix-word-rubout', mode='command')
+# config.bind('<Ctrl-Y>', 'rl-yank', mode='command')
+# config.bind('<Down>', 'completion-item-focus --history next', mode='command')
+# config.bind('<Escape>', 'leave-mode', mode='command')
+# config.bind('<Return>', 'command-accept', mode='command')
+# config.bind('<Shift-Delete>', 'completion-item-del', mode='command')
+# config.bind('<Shift-Tab>', 'completion-item-focus prev', mode='command')
+# config.bind('<Tab>', 'completion-item-focus next', mode='command')
+# config.bind('<Up>', 'completion-item-focus --history prev', mode='command')
+
+## Bindings for hint mode
+# config.bind('<Ctrl-B>', 'hint all tab-bg', mode='hint')
+# config.bind('<Ctrl-F>', 'hint links', mode='hint')
+# config.bind('<Ctrl-R>', 'hint --rapid links tab-bg', mode='hint')
+# config.bind('<Escape>', 'leave-mode', mode='hint')
+# config.bind('<Return>', 'follow-hint', mode='hint')
+
+## Bindings for insert mode
+# config.bind('<Ctrl-E>', 'open-editor', mode='insert')
+# config.bind('<Escape>', 'leave-mode', mode='insert')
+# config.bind('<Shift-Ins>', 'insert-text {primary}', mode='insert')
+config.bind('<Ctrl-i>', 'spawn --userscript qute-keepass -p ~/Documents/20151120.kdbx', mode='insert')
+
+## Bindings for passthrough mode
+# config.bind('<Ctrl-V>', 'leave-mode', mode='passthrough')
+
+## Bindings for prompt mode
+# config.bind('<Alt-B>', 'rl-backward-word', mode='prompt')
+# config.bind('<Alt-Backspace>', 'rl-backward-kill-word', mode='prompt')
+# config.bind('<Alt-D>', 'rl-kill-word', mode='prompt')
+# config.bind('<Alt-F>', 'rl-forward-word', mode='prompt')
+# config.bind('<Alt-Shift-Y>', 'prompt-yank --sel', mode='prompt')
+# config.bind('<Alt-Y>', 'prompt-yank', mode='prompt')
+# config.bind('<Ctrl-?>', 'rl-delete-char', mode='prompt')
+# config.bind('<Ctrl-A>', 'rl-beginning-of-line', mode='prompt')
+# config.bind('<Ctrl-B>', 'rl-backward-char', mode='prompt')
+# config.bind('<Ctrl-E>', 'rl-end-of-line', mode='prompt')
+# config.bind('<Ctrl-F>', 'rl-forward-char', mode='prompt')
+# config.bind('<Ctrl-H>', 'rl-backward-delete-char', mode='prompt')
+# config.bind('<Ctrl-K>', 'rl-kill-line', mode='prompt')
+# config.bind('<Ctrl-U>', 'rl-unix-line-discard', mode='prompt')
+# config.bind('<Ctrl-W>', 'rl-unix-word-rubout', mode='prompt')
+# config.bind('<Ctrl-X>', 'prompt-open-download', mode='prompt')
+# config.bind('<Ctrl-Y>', 'rl-yank', mode='prompt')
+# config.bind('<Down>', 'prompt-item-focus next', mode='prompt')
+# config.bind('<Escape>', 'leave-mode', mode='prompt')
+# config.bind('<Return>', 'prompt-accept', mode='prompt')
+# config.bind('<Shift-Tab>', 'prompt-item-focus prev', mode='prompt')
+# config.bind('<Tab>', 'prompt-item-focus next', mode='prompt')
+# config.bind('<Up>', 'prompt-item-focus prev', mode='prompt')
+
+## Bindings for register mode
+# config.bind('<Escape>', 'leave-mode', mode='register')
+
+## Bindings for yesno mode
+# config.bind('<Alt-Shift-Y>', 'prompt-yank --sel', mode='yesno')
+# config.bind('<Alt-Y>', 'prompt-yank', mode='yesno')
+# config.bind('<Escape>', 'leave-mode', mode='yesno')
+# config.bind('<Return>', 'prompt-accept', mode='yesno')
+# config.bind('n', 'prompt-accept no', mode='yesno')
+# config.bind('y', 'prompt-accept yes', mode='yesno')
+config.source('nord-qutebrowser.py')
diff --git a/.config/qutebrowser/jupyter-profile/autoconfig.yml b/.config/qutebrowser/jupyter-profile/autoconfig.yml
new file mode 100644
index 0000000..bdf84b0
--- /dev/null
+++ b/.config/qutebrowser/jupyter-profile/autoconfig.yml
@@ -0,0 +1,7 @@
+# DO NOT edit this file by hand, qutebrowser will overwrite it.
+# Instead, create a config.py - see :help for details.
+
+config_version: 2
+settings:
+ content.javascript.enabled:
+ '*://*.next-episode.net/*': false
diff --git a/.config/qutebrowser/jupyter-profile/config.py b/.config/qutebrowser/jupyter-profile/config.py
new file mode 100644
index 0000000..de9b2e7
--- /dev/null
+++ b/.config/qutebrowser/jupyter-profile/config.py
@@ -0,0 +1,1676 @@
+## Autogenerated config.py
+## Documentation:
+## qute://help/configuring.html
+## qute://help/settings.html
+#
+# Read ~/.Xresources file for colours (see the help file listed above)
+# import subprocess
+
+# def read_xresources(prefix):
+ # props = {}
+ # x=subprocess.run(['xrdb','-query'], stdout=subprocess.PIPE)
+ # lines = x.stdout.decode().split('\n')
+ # for line in filter(lambda l : l.startswith(prefix), lines):
+ # prop, _, value = line.partition(':\t')
+ # props[prop] = value
+ # return props
+
+# xresources = read_xresources('*')
+# c.colors.statusbar.normal.bg = xresources['*.background']
+
+
+## This is here so configs done via the GUI are still loaded.
+## Remove it to not load settings done via the GUI.
+# config.load_autoconfig()
+
+## Aliases for commands. The keys of the given dictionary are the
+## aliases, while the values are the commands they map to.
+## Type: Dict
+c.aliases = { 'w':'session-save', 'q':'quit', 'wq':'quit --save', 'qtb':'open https://www.qutebrowser.org/', 'mbt':'open https://signin1.bt.com/login/emailloginform', 'Ombt':'open -t https://signin1.bt.com/login/emailloginform', 'nx':'open https://next-episode.net/', 'Onx':'open -t https://next-episode.net/', 'gk':'open https://gameknot.com/', 'Ogk':'open -t https://gameknot.com/', 'yt':'open https://youtube.com', 'Oyt':'open -t https://youtube.com', 'Y':'open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files', 'hx':'open https://heasarc.gsfc.nasa.gov/', 'Ohx':'open -t https://heasarc.gsfc.nasa.gov/', 'yfy':'open https://yts.am/', '1337':'open https://1337x.to/home/', 'O1337':'open -t https://1337x.to/home/', 'ez':'open https://eztv.ag', 'Oez':'open -t https://eztv.ag', 'tz':'open https://extratorrent.ag', 'Otz':'open -t https://extratorrent.ag', 'tpb':'open https://thepiratebay.org/', 'Otpb':'open -t https://thepiratebay.org/', 'Nm':'open https://netmail.herts.ac.uk/', 'ONm':'open -t https://netmail.herts.ac.uk/', 'Oan':'open -t https://neonalley.com','Ocm':'open -t https://churchmilitant.com','Osp':'open -t https://stpaulcenter.com'}
+#
+## Time interval (in milliseconds) between auto-saves of
+## config/cookies/etc.
+## Type: Int
+# c.auto_save.interval = 15000
+
+## Always restore open sites when qutebrowser is reopened.
+## Type: Bool
+c.auto_save.session = True
+
+## Backend to use to display websites. qutebrowser supports two different
+## web rendering engines / backends, QtWebKit and QtWebEngine. QtWebKit
+## was discontinued by the Qt project with Qt 5.6, but picked up as a
+## well maintained fork: https://github.com/annulen/webkit/wiki -
+## qutebrowser only supports the fork. QtWebEngine is Qt's official
+## successor to QtWebKit. It's slightly more resource hungry than
+## QtWebKit and has a couple of missing features in qutebrowser, but is
+## generally the preferred choice.
+## Type: String
+## Valid values:
+## - webengine: Use QtWebEngine (based on Chromium).
+## - webkit: Use QtWebKit (based on WebKit, similar to Safari).
+# c.backend = 'webengine'
+
+## This setting can be used to map keys to other keys. When the key used
+## as dictionary-key is pressed, the binding for the key used as
+## dictionary-value is invoked instead. This is useful for global
+## remappings of keys, for example to map Ctrl-[ to Escape. Note that
+## when a key is bound (via `bindings.default` or `bindings.commands`),
+## the mapping is ignored.
+## Type: Dict
+# c.bindings.key_mappings = {'<Ctrl-[>': '<Escape>', '<Ctrl-6>': '<Ctrl-^>', '<Ctrl-M>': '<Return>', '<Ctrl-J>': '<Return>', '<Shift-Return>': '<Return>', '<Enter>': '<Return>', '<Shift-Enter>': '<Return>', '<Ctrl-Enter>': '<Ctrl-Return>'}
+
+## Background color of the completion widget category headers.
+## Type: QssColor
+# c.colors.completion.category.bg = 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050)'
+
+## Bottom border color of the completion widget category headers.
+## Type: QssColor
+# c.colors.completion.category.border.bottom = 'black'
+
+## Top border color of the completion widget category headers.
+## Type: QssColor
+# c.colors.completion.category.border.top = 'black'
+
+## Foreground color of completion widget category headers.
+## Type: QtColor
+# c.colors.completion.category.fg = 'white'
+
+## Background color of the completion widget for even rows.
+## Type: QssColor
+# c.colors.completion.even.bg = '#333333'
+
+## Text color of the completion widget. May be a single color to use for
+## all columns or a list of three colors, one for each column.
+## Type: List of QtColor, or QtColor
+# c.colors.completion.fg = ['white', 'white', 'white']
+
+## Background color of the selected completion item.
+## Type: QssColor
+# c.colors.completion.item.selected.bg = '#e8c000'
+
+## Bottom border color of the selected completion item.
+## Type: QssColor
+# c.colors.completion.item.selected.border.bottom = '#bbbb00'
+
+## Top border color of the completion widget category headers.
+## Type: QssColor
+# c.colors.completion.item.selected.border.top = '#bbbb00'
+
+## Foreground color of the selected completion item.
+## Type: QtColor
+# c.colors.completion.item.selected.fg = 'black'
+
+## Foreground color of the matched text in the completion.
+## Type: QssColor
+# c.colors.completion.match.fg = '#ff4444'
+
+## Background color of the completion widget for odd rows.
+## Type: QssColor
+# c.colors.completion.odd.bg = '#444444'
+
+## Color of the scrollbar in the completion view.
+## Type: QssColor
+# c.colors.completion.scrollbar.bg = '#333333'
+
+## Color of the scrollbar handle in the completion view.
+## Type: QssColor
+# c.colors.completion.scrollbar.fg = 'white'
+
+## Background color for the download bar.
+## Type: QssColor
+# c.colors.downloads.bar.bg = 'black'
+
+## Background color for downloads with errors.
+## Type: QtColor
+# c.colors.downloads.error.bg = 'red'
+
+## Foreground color for downloads with errors.
+## Type: QtColor
+# c.colors.downloads.error.fg = 'white'
+
+## Color gradient start for download backgrounds.
+## Type: QtColor
+# c.colors.downloads.start.bg = '#0000aa'
+
+## Color gradient start for download text.
+## Type: QtColor
+# c.colors.downloads.start.fg = 'white'
+
+## Color gradient stop for download backgrounds.
+## Type: QtColor
+# c.colors.downloads.stop.bg = '#00aa00'
+
+## Color gradient end for download text.
+## Type: QtColor
+# c.colors.downloads.stop.fg = 'white'
+
+## Color gradient interpolation system for download backgrounds.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+# c.colors.downloads.system.bg = 'rgb'
+
+## Color gradient interpolation system for download text.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+# c.colors.downloads.system.fg = 'rgb'
+
+## Background color for hints. Note that you can use a `rgba(...)` value
+## for transparency.
+## Type: QssColor
+# c.colors.hints.bg = 'qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8))'
+
+## Font color for hints.
+## Type: QssColor
+# c.colors.hints.fg = 'black'
+
+## Font color for the matched part of hints.
+## Type: QssColor
+# c.colors.hints.match.fg = 'green'
+
+## Background color of the keyhint widget.
+## Type: QssColor
+# c.colors.keyhint.bg = 'rgba(0, 0, 0, 80%)'
+
+## Text color for the keyhint widget.
+## Type: QssColor
+# c.colors.keyhint.fg = '#FFFFFF'
+
+## Highlight color for keys to complete the current keychain.
+## Type: QssColor
+# c.colors.keyhint.suffix.fg = '#FFFF00'
+
+## Background color of an error message.
+## Type: QssColor
+# c.colors.messages.error.bg = 'red'
+
+## Border color of an error message.
+## Type: QssColor
+# c.colors.messages.error.border = '#bb0000'
+
+## Foreground color of an error message.
+## Type: QssColor
+# c.colors.messages.error.fg = 'white'
+
+## Background color of an info message.
+## Type: QssColor
+# c.colors.messages.info.bg = 'black'
+
+## Border color of an info message.
+## Type: QssColor
+# c.colors.messages.info.border = '#333333'
+
+## Foreground color of an info message.
+## Type: QssColor
+# c.colors.messages.info.fg = 'white'
+
+## Background color of a warning message.
+## Type: QssColor
+# c.colors.messages.warning.bg = 'darkorange'
+
+## Border color of a warning message.
+## Type: QssColor
+# c.colors.messages.warning.border = '#d47300'
+
+## Foreground color of a warning message.
+## Type: QssColor
+# c.colors.messages.warning.fg = 'white'
+
+## Background color for prompts.
+## Type: QssColor
+# c.colors.prompts.bg = '#444444'
+
+## Border used around UI elements in prompts.
+## Type: String
+# c.colors.prompts.border = '1px solid gray'
+
+## Foreground color for prompts.
+## Type: QssColor
+# c.colors.prompts.fg = 'white'
+
+## Background color for the selected item in filename prompts.
+## Type: QssColor
+# c.colors.prompts.selected.bg = 'grey'
+
+## Background color of the statusbar in caret mode.
+## Type: QssColor
+# c.colors.statusbar.caret.bg = 'purple'
+
+## Foreground color of the statusbar in caret mode.
+## Type: QssColor
+# c.colors.statusbar.caret.fg = 'white'
+
+## Background color of the statusbar in caret mode with a selection.
+## Type: QssColor
+# c.colors.statusbar.caret.selection.bg = '#a12dff'
+
+## Foreground color of the statusbar in caret mode with a selection.
+## Type: QssColor
+# c.colors.statusbar.caret.selection.fg = 'white'
+
+## Background color of the statusbar in command mode.
+## Type: QssColor
+# c.colors.statusbar.command.bg = 'black'
+
+## Foreground color of the statusbar in command mode.
+## Type: QssColor
+# c.colors.statusbar.command.fg = 'white'
+
+## Background color of the statusbar in private browsing + command mode.
+## Type: QssColor
+# c.colors.statusbar.command.private.bg = 'grey'
+
+## Foreground color of the statusbar in private browsing + command mode.
+## Type: QssColor
+# c.colors.statusbar.command.private.fg = 'white'
+
+## Background color of the statusbar in insert mode.
+## Type: QssColor
+# c.colors.statusbar.insert.bg = 'darkgreen'
+
+## Foreground color of the statusbar in insert mode.
+## Type: QssColor
+# c.colors.statusbar.insert.fg = 'white'
+
+## Background color of the statusbar.
+## Type: QssColor
+# c.colors.statusbar.normal.bg = 'black'
+
+## Foreground color of the statusbar.
+## Type: QssColor
+# c.colors.statusbar.normal.fg = 'white'
+
+## Background color of the statusbar in passthrough mode.
+## Type: QssColor
+# c.colors.statusbar.passthrough.bg = 'darkblue'
+
+## Foreground color of the statusbar in passthrough mode.
+## Type: QssColor
+# c.colors.statusbar.passthrough.fg = 'white'
+
+## Background color of the statusbar in private browsing mode.
+## Type: QssColor
+# c.colors.statusbar.private.bg = '#666666'
+
+## Foreground color of the statusbar in private browsing mode.
+## Type: QssColor
+# c.colors.statusbar.private.fg = 'white'
+
+## Background color of the progress bar.
+## Type: QssColor
+# c.colors.statusbar.progress.bg = 'white'
+
+## Foreground color of the URL in the statusbar on error.
+## Type: QssColor
+# c.colors.statusbar.url.error.fg = 'orange'
+
+## Default foreground color of the URL in the statusbar.
+## Type: QssColor
+# c.colors.statusbar.url.fg = 'white'
+
+## Foreground color of the URL in the statusbar for hovered links.
+## Type: QssColor
+# c.colors.statusbar.url.hover.fg = 'aqua'
+
+## Foreground color of the URL in the statusbar on successful load
+## (http).
+## Type: QssColor
+# c.colors.statusbar.url.success.http.fg = 'white'
+
+## Foreground color of the URL in the statusbar on successful load
+## (https).
+## Type: QssColor
+# c.colors.statusbar.url.success.https.fg = 'lime'
+
+## Foreground color of the URL in the statusbar when there's a warning.
+## Type: QssColor
+# c.colors.statusbar.url.warn.fg = 'yellow'
+
+## Background color of the tab bar.
+## Type: QtColor
+# c.colors.tabs.bar.bg = '#555555'
+
+## Background color of unselected even tabs.
+## Type: QtColor
+# c.colors.tabs.even.bg = 'darkgrey'
+
+## Foreground color of unselected even tabs.
+## Type: QtColor
+# c.colors.tabs.even.fg = 'white'
+
+## Color for the tab indicator on errors.
+## Type: QtColor
+# c.colors.tabs.indicator.error = '#ff0000'
+
+## Color gradient start for the tab indicator.
+## Type: QtColor
+# c.colors.tabs.indicator.start = '#0000aa'
+
+## Color gradient end for the tab indicator.
+## Type: QtColor
+# c.colors.tabs.indicator.stop = '#00aa00'
+
+## Color gradient interpolation system for the tab indicator.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+# c.colors.tabs.indicator.system = 'rgb'
+
+## Background color of unselected odd tabs.
+## Type: QtColor
+# c.colors.tabs.odd.bg = 'grey'
+
+## Foreground color of unselected odd tabs.
+## Type: QtColor
+# c.colors.tabs.odd.fg = 'white'
+
+## Background color of selected even tabs.
+## Type: QtColor
+# c.colors.tabs.selected.even.bg = 'black'
+
+## Foreground color of selected even tabs.
+## Type: QtColor
+# c.colors.tabs.selected.even.fg = 'white'
+
+## Background color of selected odd tabs.
+## Type: QtColor
+# c.colors.tabs.selected.odd.bg = 'black'
+
+## Foreground color of selected odd tabs.
+## Type: QtColor
+# c.colors.tabs.selected.odd.fg = 'white'
+
+## Background color for webpages if unset (or empty to use the theme's
+## color).
+## Type: QtColor
+# c.colors.webpage.bg = 'white'
+
+## Number of commands to save in the command history. 0: no history / -1:
+## unlimited
+## Type: Int
+# c.completion.cmd_history_max_items = 100
+
+## Delay (in milliseconds) before updating completions after typing a
+## character.
+## Type: Int
+# c.completion.delay = 0
+
+## Height (in pixels or as percentage of the window) of the completion.
+## Type: PercOrInt
+# c.completion.height = '50%'
+
+## Minimum amount of characters needed to update completions.
+## Type: Int
+# c.completion.min_chars = 1
+
+## Move on to the next part when there's only one possible completion
+## left.
+## Type: Bool
+# c.completion.quick = True
+
+## Padding (in pixels) of the scrollbar handle in the completion window.
+## Type: Int
+# c.completion.scrollbar.padding = 2
+
+## Width (in pixels) of the scrollbar in the completion window.
+## Type: Int
+# c.completion.scrollbar.width = 12
+
+## When to show the autocompletion window.
+## Type: String
+## Valid values:
+## - always: Whenever a completion is available.
+## - auto: Whenever a completion is requested.
+## - never: Never.
+# c.completion.show = 'always'
+
+## Shrink the completion to be smaller than the configured size if there
+## are no scrollbars.
+## Type: Bool
+# c.completion.shrink = False
+
+## Format of timestamps (e.g. for the history completion).
+## Type: TimestampTemplate
+# c.completion.timestamp_format = '%Y-%m-%d'
+
+## Execute the best-matching command on a partial match.
+## Type: Bool
+c.completion.use_best_match = True
+
+## Number of URLs to show in the web history. 0: no history / -1:
+## unlimited
+## Type: Int
+# c.completion.web_history_max_items = -1
+
+## Require a confirmation before quitting the application.
+## Type: ConfirmQuit
+## Valid values:
+## - always: Always show a confirmation.
+## - multiple-tabs: Show a confirmation if multiple tabs are opened.
+## - downloads: Show a confirmation if downloads are running
+## - never: Never show a confirmation.
+# c.confirm_quit = ['never']
+
+## 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.
+## Type: Bool
+# c.content.cache.appcache = True
+
+## Maximum number of pages to hold in the global memory page cache. The
+## page cache allows for a nicer user experience when navigating forth or
+## back to pages in the forward/back history, by pausing and resuming up
+## to _n_ pages. For more information about the feature, please refer to:
+## http://webkit.org/blog/427/webkit-page-cache-i-the-basics/
+## Type: Int
+# c.content.cache.maximum_pages = 0
+
+## Size (in bytes) of the HTTP network cache. Null to use the default
+## value. With QtWebEngine, the maximum supported value is 2147483647 (~2
+## GB).
+## Type: Int
+# c.content.cache.size = None
+
+## Which cookies to accept.
+## Type: String
+## Valid values:
+## - all: Accept all cookies.
+## - no-3rdparty: Accept cookies from the same origin only.
+## - no-unknown-3rdparty: Accept cookies from the same origin only, unless a cookie is already set for the domain.
+## - never: Don't accept cookies at all.
+# c.content.cookies.accept = 'no-3rdparty'
+
+## Store cookies. Note this option needs a restart with QtWebEngine on Qt
+## < 5.9.
+## Type: Bool
+c.content.cookies.store = True
+
+## Default encoding to use for websites. The encoding must be a string
+## describing an encoding such as _utf-8_, _iso-8859-1_, etc.
+## Type: String
+# c.content.default_encoding = 'iso-8859-1'
+
+## Enable extra tools for Web developers. This needs to be enabled for
+## `:inspector` to work and also adds an _Inspect_ entry to the context
+## menu. For QtWebEngine, see `--enable-webengine-inspector` in
+## `qutebrowser --help` instead.
+## Type: Bool
+## c.content.developer_extras = True
+
+## Try to pre-fetch DNS entries to speed up browsing.
+## Type: Bool
+# c.content.dns_prefetch = True
+
+## Expand each subframe to its contents. This will flatten all the frames
+## to become one scrollable page.
+## Type: Bool
+# c.content.frame_flattening = False
+
+## Allow websites to request geolocations.
+## Type: BoolAsk
+## Valid values:
+## - true
+## - false
+## - ask
+# c.content.geolocation = 'ask'
+
+## Value to send in the `Accept-Language` header.
+## Type: String
+# c.content.headers.accept_language = 'en-US,en'
+
+## Custom headers for qutebrowser HTTP requests.
+## Type: Dict
+# c.content.headers.custom = {}
+
+## 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.
+## Type: Bool
+# c.content.headers.do_not_track = True
+
+## When to send the Referer header. The Referer header tells websites
+## from which website you were coming from when visiting them.
+## Type: String
+## Valid values:
+## - always: Always send the Referer.
+## - never: Never send the Referer. This is not recommended, as some sites may break.
+## - same-domain: Only send the Referer for the same domain. This will still protect your privacy, but shouldn't break any sites.
+# c.content.headers.referer = 'same-domain'
+
+## User agent to send. Unset to send the default.
+## Type: String
+# c.content.headers.user_agent = None
+
+## Enable host blocking.
+## Type: Bool
+# c.content.host_blocking.enabled = True
+
+## List of URLs of lists which contain hosts to block. The file can be
+## in one of the following formats: - An `/etc/hosts`-like file - One
+## host per line - A zip-file of any of the above, with either only one
+## file, or a file named `hosts` (with any extension).
+## Type: List of Url
+# c.content.host_blocking.lists = ['https://www.malwaredomainlist.com/hostslist/hosts.txt', 'http://someonewhocares.org/hosts/hosts', 'http://winhelp2002.mvps.org/hosts.zip', 'http://malwaredomains.lehigh.edu/files/justdomains.zip', 'https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext']
+
+## List of domains that should always be loaded, despite being ad-
+## blocked. Domains may contain * and ? wildcards and are otherwise
+## required to exactly match the requested domain. Local domains are
+## always exempt from hostblocking.
+## Type: List of String
+c.content.host_blocking.whitelist = ['piwik.org','next-episode.net','churchmilitant.com','churchmilitant.tv']
+
+## Enable hyperlink auditing (`<a ping>`).
+## Type: Bool
+# c.content.hyperlink_auditing = False
+
+## Load images automatically in web pages.
+## Type: Bool
+# c.content.images = True
+
+## Show javascript alerts.
+## Type: Bool
+# c.content.javascript.alert = True
+
+## Allow JavaScript to read from or write to the clipboard. With
+## QtWebEngine, writing the clipboard as response to a user interaction
+## is always allowed.
+## Type: Bool
+# c.content.javascript.can_access_clipboard = False
+
+## Allow JavaScript to close tabs.
+## Type: Bool
+# c.content.javascript.can_close_tabs = False
+
+## Allow JavaScript to open new tabs without user interaction.
+## Type: Bool
+# c.content.javascript.can_open_tabs_automatically = False
+
+## Enable JavaScript.
+## Type: Bool
+c.content.javascript.enabled = True
+
+## Log levels to use for JavaScript console logging messages. When a
+## JavaScript message with the level given in the dictionary key is
+## logged, the corresponding dictionary value selects the qutebrowser
+## logger to use. On QtWebKit, the "unknown" setting is always used.
+## Type: Dict
+# c.content.javascript.log = {'unknown': 'debug', 'info': 'debug', 'warning': 'debug', 'error': 'debug'}
+
+## Use the standard JavaScript modal dialog for `alert()` and
+## `confirm()`.
+## Type: Bool
+# c.content.javascript.modal_dialog = False
+
+## Show javascript prompts.
+## Type: Bool
+# c.content.javascript.prompt = True
+
+## Allow locally loaded documents to access other local URLs.
+## Type: Bool
+# c.content.local_content_can_access_file_urls = True
+
+## Allow locally loaded documents to access remote URLs.
+## Type: Bool
+# c.content.local_content_can_access_remote_urls = False
+
+## Enable support for HTML 5 local storage and Web SQL.
+## Type: Bool
+# c.content.local_storage = True
+
+## Allow websites to record audio/video.
+## Type: BoolAsk
+## Valid values:
+## - true
+## - false
+## - ask
+# c.content.media_capture = 'ask'
+
+## Netrc-file for HTTP authentication. If unset, `~/.netrc` is used.
+## Type: File
+# c.content.netrc_file = None
+
+## Allow websites to show notifications.
+## Type: BoolAsk
+## Valid values:
+## - true
+## - false
+## - ask
+# c.content.notifications = 'ask'
+
+## 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.
+## Type: Bool
+# c.content.pdfjs = False
+
+## Enable plugins in Web pages.
+## Type: Bool
+# c.content.plugins = False vgg
+c.content.plugins = True
+
+## Draw the background color and images also when the page is printed.
+## Type: Bool
+# c.content.print_element_backgrounds = True
+
+## Open new windows in private browsing mode which does not record
+## visited pages.
+## Type: Bool
+# c.content.private_browsing = False
+
+## Proxy to use. In addition to the listed values, you can use a
+## `socks://...` or `http://...` URL.
+## Type: Proxy
+## Valid values:
+## - system: Use the system wide proxy.
+## - none: Don't use any proxy
+# c.content.proxy = 'system'
+
+## Send DNS requests over the configured proxy.
+## Type: Bool
+# c.content.proxy_dns_requests = True
+
+## Validate SSL handshakes.
+## Type: BoolAsk
+## Valid values:
+## - true
+## - false
+## - ask
+# c.content.ssl_strict = 'ask'
+
+## List of user stylesheet filenames to use.
+## Type: List of File, or File
+# c.content.user_stylesheets = []
+
+## Enable WebGL.
+## Type: Bool
+# c.content.webgl = True
+
+## Limit fullscreen to the browser window (does not expand to fill the
+## screen).
+## Type: Bool
+# c.content.windowed_fullscreen = False
+
+## Monitor load requests for cross-site scripting attempts. Suspicious
+## scripts will be blocked and reported in the inspector's JavaScript
+## console. Enabling this feature might have an impact on performance.
+## Type: Bool
+# c.content.xss_auditing = False
+
+## Directory to save downloads to. If unset, a sensible OS-specific
+## default is used.
+## Type: Directory
+# c.downloads.location.directory = None
+
+## Prompt the user for the download location. If set to false,
+## `downloads.location.directory` will be used.
+## Type: Bool
+# c.downloads.location.prompt = True
+
+## Remember the last used download directory.
+## Type: Bool
+# c.downloads.location.remember = True
+
+## What to display in the download filename input.
+## Type: String
+## Valid values:
+## - path: Show only the download path.
+## - filename: Show only download filename.
+## - both: Show download path and filename.
+# c.downloads.location.suggestion = 'path'
+
+## Default program used to open downloads. If null, the default internal
+## handler is used. Any `{}` in the string will be expanded to the
+## filename, else the filename will be appended.
+## Type: String
+# c.downloads.open_dispatcher = None
+
+## Where to show the downloaded files.
+## Type: VerticalPosition
+## Valid values:
+## - top
+## - bottom
+# c.downloads.position = 'top'
+
+## Duration (in milliseconds) to wait before removing finished downloads.
+## If set to -1, downloads are never removed.
+## Type: Int
+# c.downloads.remove_finished = -1
+
+## Editor (and arguments) to use for the `open-editor` command. The
+## following placeholders are defined: * `{file}`: Filename of the file
+## to be edited. * `{line}`: Line in which the caret is found in the
+## text. * `{column}`: Column in which the caret is found in the text. *
+## `{line0}`: Same as `{line}`, but starting from index 0. * `{column0}`:
+## Same as `{column}`, but starting from index 0.
+## Type: ShellCommand
+# c.editor.command = ['gvim', '-f', '{file}', '-c', 'normal {line}G{column0}l']
+c.editor.command = ["urxvt", "-title", "qutebrowserSratchpad", "-geometry", "86x24+40+60", "-e", "vim", "-f", "{}"]
+
+## Encoding to use for the editor.
+## Type: Encoding
+# c.editor.encoding = 'utf-8'
+
+## Font used in the completion categories.
+## Type: Font
+# c.fonts.completion.category = 'bold 10pt monospace'
+
+## Font used in the completion widget.
+## Type: Font
+# c.fonts.completion.entry = '10pt monospace'
+
+## Font used for the debugging console.
+## Type: QtFont
+# c.fonts.debug_console = '10pt monospace'
+
+## Font used for the downloadbar.
+## Type: Font
+# c.fonts.downloads = '10pt monospace'
+
+## Font used for the hints.
+## Type: Font
+c.fonts.hints = 'bold 8pt monospace'
+
+## Font used in the keyhint widget.
+## Type: Font
+# c.fonts.keyhint = '10pt monospace'
+
+## Font used for error messages.
+## Type: Font
+# c.fonts.messages.error = '10pt monospace'
+
+## Font used for info messages.
+## Type: Font
+# c.fonts.messages.info = '10pt monospace'
+
+## Font used for warning messages.
+## Type: Font
+# c.fonts.messages.warning = '10pt monospace'
+
+## Default monospace fonts. Whenever "monospace" is used in a font
+## setting, it's replaced with the fonts listed here.
+## Type: Font
+# c.fonts.monospace = '"xos4 Terminus", Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal'
+
+## Font used for prompts.
+## Type: Font
+# c.fonts.prompts = '10pt sans-serif'
+
+## Font used in the statusbar.
+## Type: Font
+# c.fonts.statusbar = '10pt monospace'
+
+## Font used in the tab bar.
+## Type: QtFont
+# c.fonts.tabs = '10pt monospace'
+
+## Font family for cursive fonts.
+## Type: FontFamily
+# c.fonts.web.family.cursive = ''
+
+## Font family for fantasy fonts.
+## Type: FontFamily
+# c.fonts.web.family.fantasy = ''
+
+## Font family for fixed fonts.
+## Type: FontFamily
+# c.fonts.web.family.fixed = ''
+
+## Font family for sans-serif fonts.
+## Type: FontFamily
+# c.fonts.web.family.sans_serif = ''
+
+## Font family for serif fonts.
+## Type: FontFamily
+# c.fonts.web.family.serif = ''
+
+## Font family for standard fonts.
+## Type: FontFamily
+# c.fonts.web.family.standard = ''
+
+## Default font size (in pixels) for regular text.
+## Type: Int
+# c.fonts.web.size.default = 16
+
+## Default font size (in pixels) for fixed-pitch text.
+## Type: Int
+# c.fonts.web.size.default_fixed = 13
+
+## Hard minimum font size (in pixels).
+## Type: Int
+# c.fonts.web.size.minimum = 0
+
+## Minimum logical font size (in pixels) that is applied when zooming
+## out.
+## Type: Int
+# c.fonts.web.size.minimum_logical = 6
+
+## When a hint can be automatically followed without pressing Enter.
+## Type: String
+## Valid values:
+## - always: Auto-follow whenever there is only a single hint on a page.
+## - unique-match: Auto-follow whenever there is a unique non-empty match in either the hint string (word mode) or filter (number mode).
+## - full-match: Follow the hint when the user typed the whole hint (letter, word or number mode) or the element's text (only in number mode).
+## - never: The user will always need to press Enter to follow a hint.
+# c.hints.auto_follow = 'unique-match'
+
+## Duration (in milliseconds) to ignore normal-mode key bindings after a
+## successful auto-follow.
+## Type: Int
+# c.hints.auto_follow_timeout = 0
+
+## CSS border value for hints.
+## Type: String
+# c.hints.border = '1px solid #E3BE23'
+
+## Characters used for hint strings.
+## Type: UniqueCharString
+# c.hints.chars = 'asdfghjkl'
+
+## Dictionary file to be used by the word hints.
+## Type: File
+# c.hints.dictionary = '/usr/share/dict/words'
+
+## Which implementation to use to find elements to hint.
+## Type: String
+## Valid values:
+## - javascript: Better but slower
+## - python: Slightly worse but faster
+# c.hints.find_implementation = 'python'
+
+## Hide unmatched hints in rapid mode.
+## Type: Bool
+# c.hints.hide_unmatched_rapid_hints = True
+
+## Minimum number of characters used for hint strings.
+## Type: Int
+# c.hints.min_chars = 1
+
+## Mode to use for hints.
+## Type: String
+## Valid values:
+## - number: Use numeric hints. (In this mode you can also type letters from the hinted element to filter and reduce the number of elements that are hinted.)
+## - letter: Use the characters in the `hints.chars` setting.
+## - word: Use hints words based on the html elements and the extra words.
+c.hints.mode = 'number'
+
+## Comma-separated list of regular expressions to use for 'next' links.
+## Type: List of Regex
+# c.hints.next_regexes = ['\\bnext\\b', '\\bmore\\b', '\\bnewer\\b', '\\b[>→≫]\\b', '\\b(>>|»)\\b', '\\bcontinue\\b']
+
+## Comma-separated list of regular expressions to use for 'prev' links.
+## Type: List of Regex
+# c.hints.prev_regexes = ['\\bprev(ious)?\\b', '\\bback\\b', '\\bolder\\b', '\\b[<←≪]\\b', '\\b(<<|«)\\b']
+
+## Scatter hint key chains (like Vimium) or not (like dwb). Ignored for
+## number hints.
+## Type: Bool
+# c.hints.scatter = True
+
+## Make characters in hint strings uppercase.
+## Type: Bool
+# c.hints.uppercase = False
+
+## Maximum time (in minutes) between two history items for them to be
+## considered being from the same browsing session. Items with less time
+## between them are grouped when being displayed in `:history`. Use -1 to
+## disable separation.
+## Type: Int
+# c.history_gap_interval = 30
+
+## Which unbound keys to forward to the webview in normal mode.
+## Type: String
+## Valid values:
+## - all: Forward all unbound keys.
+## - auto: Forward unbound non-alphanumeric keys.
+## - none: Don't forward any keys.
+# c.input.forward_unbound_keys = 'auto'
+
+## Enter insert mode if an editable element is clicked.
+## Type: Bool
+# c.input.insert_mode.auto_enter = True
+
+## Leave insert mode if a non-editable element is clicked.
+## Type: Bool
+# c.input.insert_mode.auto_leave = True
+
+## Automatically enter insert mode if an editable element is focused
+## after loading the page.
+## Type: Bool
+# c.input.insert_mode.auto_load = False
+
+## Switch to insert mode when clicking flash and other plugins.
+## Type: Bool
+# c.input.insert_mode.plugins = False
+
+## Include hyperlinks in the keyboard focus chain when tabbing.
+## Type: Bool
+# c.input.links_included_in_focus_chain = True
+
+## Timeout (in milliseconds) for partially typed key bindings. If the
+## current input forms only partial matches, the keystring will be
+## cleared after this time.
+## Type: Int
+# c.input.partial_timeout = 5000
+
+## Enable Opera-like mouse rocker gestures. This disables the context
+## menu.
+## Type: Bool
+# c.input.rocker_gestures = False
+
+## 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.
+## Type: Bool
+# c.input.spatial_navigation = False
+
+## Keychains that shouldn't be shown in the keyhint dialog. Globs are
+## supported, so `;*` will blacklist all keychains starting with `;`. Use
+## `*` to disable keyhints.
+## Type: List of String
+# c.keyhint.blacklist = []
+
+## Time (in milliseconds) from pressing a key to seeing the keyhint
+## dialog.
+## Type: Int
+# c.keyhint.delay = 500
+
+## Rounding radius (in pixels) for the edges of the keyhint dialog.
+## Type: Int
+# c.keyhint.radius = 6
+
+## Duration (in milliseconds) to show messages in the statusbar for. Set
+## to 0 to never clear messages.
+## Type: Int
+# c.messages.timeout = 2000
+
+## How to open links in an existing instance if a new one is launched.
+## This happens when e.g. opening a link from a terminal. See
+## `new_instance_open_target_window` to customize in which window the
+## link is opened in.
+## Type: String
+## Valid values:
+## - tab: Open a new tab in the existing window and activate the window.
+## - tab-bg: Open a new background tab in the existing window and activate the window.
+## - tab-silent: Open a new tab in the existing window without activating the window.
+## - tab-bg-silent: Open a new background tab in the existing window without activating the window.
+## - window: Open in a new window.
+# c.new_instance_open_target = 'tab'
+
+## Which window to choose when opening links as new tabs. When
+## `new_instance_open_target` is not set to `window`, this is ignored.
+## Type: String
+## Valid values:
+## - first-opened: Open new tabs in the first (oldest) opened window.
+## - last-opened: Open new tabs in the last (newest) opened window.
+## - last-focused: Open new tabs in the most recently focused window.
+## - last-visible: Open new tabs in the most recently visible window.
+# c.new_instance_open_target_window = 'last-focused'
+
+## Show a filebrowser in upload/download prompts.
+## Type: Bool
+# c.prompt.filebrowser = True
+
+## Rounding radius (in pixels) for the edges of prompts.
+## Type: Int
+# c.prompt.radius = 8
+
+## Additional arguments to pass to Qt, without leading `--`. With
+## QtWebEngine, some Chromium arguments (see
+## https://peter.sh/experiments/chromium-command-line-switches/ for a
+## list) will work.
+## Type: List of String
+# c.qt.args = []
+
+## Force a Qt platform to use. This sets the `QT_QPA_PLATFORM`
+## environment variable and is useful to force using the XCB plugin when
+## running QtWebEngine on Wayland.
+## Type: String
+# c.qt.force_platform = None
+
+## Force software rendering for QtWebEngine. This is needed for
+## QtWebEngine to work with Nouveau drivers.
+## Type: Bool
+# c.qt.force_software_rendering = False
+
+## Turn on Qt HighDPI scaling. This is equivalent to setting
+## QT_AUTO_SCREEN_SCALE_FACTOR=1 in the environment. It's off by default
+## as it can cause issues with some bitmap fonts. As an alternative to
+## this, it's possible to set font sizes and the `zoom.default` setting.
+## Type: Bool
+# c.qt.highdpi = False
+
+## Show a scrollbar.
+## Type: Bool
+# c.scrolling.bar = False
+
+## Enable smooth scrolling for web pages. Note smooth scrolling does not
+## work with the `:scroll-px` command.
+## Type: Bool
+# c.scrolling.smooth = False
+
+## When to find text on a page case-insensitively.
+## Type: String
+## Valid values:
+## - always: Search case-insensitively.
+## - never: Search case-sensitively.
+## - smart: Search case-sensitively if there are capital characters.
+# c.search.ignore_case = 'smart'
+
+## Find text on a page incrementally, renewing the search for each typed
+## character.
+## Type: Bool
+# c.search.incremental = True
+
+## Name of the session to save by default. If this is set to null, the
+## session which was last loaded is saved.
+## Type: SessionName
+# c.session.default_name = None
+
+## Load a restored tab as soon as it takes focus.
+## Type: Bool
+# c.session.lazy_restore = False
+
+## Languages to use for spell checking. You can check for available
+## languages and install dictionaries using scripts/dictcli.py. Run the
+## script with -h/--help for instructions.
+## Type: List of String
+## Valid values:
+## - af-ZA: Afrikaans (South Africa)
+## - bg-BG: Bulgarian (Bulgaria)
+## - ca-ES: Catalan (Spain)
+## - cs-CZ: Czech (Czech Republic)
+## - da-DK: Danish (Denmark)
+## - de-DE: German (Germany)
+## - el-GR: Greek (Greece)
+## - en-AU: English (Australia)
+## - en-CA: English (Canada)
+## - en-GB: English (United Kingdom)
+## - en-US: English (United States)
+## - es-ES: Spanish (Spain)
+## - et-EE: Estonian (Estonia)
+## - fa-IR: Farsi (Iran)
+## - fo-FO: Faroese (Faroe Islands)
+## - fr-FR: French (France)
+## - he-IL: Hebrew (Israel)
+## - hi-IN: Hindi (India)
+## - hr-HR: Croatian (Croatia)
+## - hu-HU: Hungarian (Hungary)
+## - id-ID: Indonesian (Indonesia)
+## - it-IT: Italian (Italy)
+## - ko: Korean
+## - lt-LT: Lithuanian (Lithuania)
+## - lv-LV: Latvian (Latvia)
+## - nb-NO: Norwegian (Norway)
+## - nl-NL: Dutch (Netherlands)
+## - pl-PL: Polish (Poland)
+## - pt-BR: Portuguese (Brazil)
+## - pt-PT: Portuguese (Portugal)
+## - ro-RO: Romanian (Romania)
+## - ru-RU: Russian (Russia)
+## - sh: Serbo-Croatian
+## - sk-SK: Slovak (Slovakia)
+## - sl-SI: Slovenian (Slovenia)
+## - sq: Albanian
+## - sr: Serbian
+## - sv-SE: Swedish (Sweden)
+## - ta-IN: Tamil (India)
+## - tg-TG: Tajik (Tajikistan)
+## - tr-TR: Turkish (Turkey)
+## - uk-UA: Ukrainian (Ukraine)
+## - vi-VN: Vietnamese (Viet Nam)
+# c.spellcheck.languages = []
+
+## Hide the statusbar unless a message is shown.
+## Type: Bool
+# c.statusbar.hide = False
+
+## Padding (in pixels) for the statusbar.
+## Type: Padding
+# c.statusbar.padding = {'top': 1, 'bottom': 1, 'left': 0, 'right': 0}
+
+## Position of the status bar.
+## Type: VerticalPosition
+## Valid values:
+## - top
+## - bottom
+# c.statusbar.position = 'bottom'
+
+## List of widgets displayed in the statusbar.
+## Type: List of String
+## Valid values:
+## - url: Current page URL.
+## - scroll: Percentage of the current page position like `10%`.
+## - scroll_raw: Raw percentage of the current page position like `10`.
+## - history: Display an arrow when possible to go back/forward in history.
+## - tabs: Current active tab, e.g. `2`.
+## - keypress: Display pressed keys when composing a vi command.
+## - progress: Progress bar for the current page loading.
+# c.statusbar.widgets = ['keypress', 'url', 'scroll', 'history', 'tabs', 'progress']
+
+## Open new tabs (middleclick/ctrl+click) in the background.
+## Type: Bool
+# c.tabs.background = False
+
+## Mouse button with which to close tabs.
+## Type: String
+## Valid values:
+## - right: Close tabs on right-click.
+## - middle: Close tabs on middle-click.
+## - none: Don't close tabs using the mouse.
+# c.tabs.close_mouse_button = 'middle'
+
+## How to behave when the close mouse button is pressed on the tab bar.
+## Type: String
+## Valid values:
+## - new-tab: Open a new tab.
+## - close-current: Close the current tab.
+## - close-last: Close the last tab.
+## - ignore: Don't do anything.
+# c.tabs.close_mouse_button_on_bar = 'new-tab'
+
+## Scaling factor for favicons in the tab bar. The tab size is unchanged,
+## so big favicons also require extra `tabs.padding`.
+## Type: Float
+# c.tabs.favicons.scale = 1.0
+
+## Show favicons in the tab bar.
+## Type: Bool
+# c.tabs.favicons.show = True
+
+## Padding (in pixels) for tab indicators.
+## Type: Padding
+# c.tabs.indicator.padding = {'top': 2, 'bottom': 2, 'left': 0, 'right': 4}
+
+## Width (in pixels) of the progress indicator (0 to disable).
+## Type: Int
+# c.tabs.indicator.width = 3
+
+## How to behave when the last tab is closed.
+## Type: String
+## Valid values:
+## - ignore: Don't do anything.
+## - blank: Load a blank page.
+## - startpage: Load the start page.
+## - default-page: Load the default page.
+## - close: Close the window.
+# c.tabs.last_close = 'ignore'
+
+## When switching tabs, what input mode is applied.
+## Type: String
+## Valid values:
+## - persist: Retain the current mode.
+## - restore: Restore previously saved mode.
+## - normal: Always revert to normal mode.
+# c.tabs.mode_on_change = 'normal'
+
+## Switch between tabs using the mouse wheel.
+## Type: Bool
+# c.tabs.mousewheel_switching = True
+
+## Position of new tabs opened from another tab.
+## Type: NewTabPosition
+## Valid values:
+## - prev: Before the current tab.
+## - next: After the current tab.
+## - first: At the beginning.
+## - last: At the end.
+# c.tabs.new_position.related = 'next'
+
+## Position of new tabs which aren't opened from another tab.
+## Type: NewTabPosition
+## Valid values:
+## - prev: Before the current tab.
+## - next: After the current tab.
+## - first: At the beginning.
+## - last: At the end.
+# c.tabs.new_position.unrelated = 'last'
+
+## Padding (in pixels) around text for tabs.
+## Type: Padding
+# c.tabs.padding = {'top': 0, 'bottom': 0, 'left': 5, 'right': 5}
+
+## Shrink pinned tabs down to their contents.
+## Type: Bool
+# c.tabs.pinned.shrink = True
+
+## Position of the tab bar.
+## Type: Position
+## Valid values:
+## - top
+## - bottom
+## - left
+## - right
+# c.tabs.position = 'top'
+
+## Which tab to select when the focused tab is removed.
+## Type: SelectOnRemove
+## Valid values:
+## - prev: Select the tab which came before the closed one (left in horizontal, above in vertical).
+## - next: Select the tab which came after the closed one (right in horizontal, below in vertical).
+## - last-used: Select the previously selected tab.
+# c.tabs.select_on_remove = 'next'
+
+## When to show the tab bar.
+## Type: String
+## Valid values:
+## - always: Always show the tab bar.
+## - never: Always hide the tab bar.
+## - multiple: Hide the tab bar if only one tab is open.
+## - switching: Show the tab bar when switching tabs.
+# c.tabs.show = 'always'
+
+## Duration (in milliseconds) to show the tab bar before hiding it when
+## tabs.show is set to 'switching'.
+## Type: Int
+# c.tabs.show_switching_delay = 800
+
+## Open a new window for every tab.
+## Type: Bool
+# c.tabs.tabs_are_windows = False
+
+## Alignment of the text inside of tabs.
+## Type: TextAlignment
+## Valid values:
+## - left
+## - right
+## - center
+# c.tabs.title.alignment = 'left'
+
+## Format to use for the tab title. The following placeholders are
+## defined: * `{perc}`: Percentage as a string like `[10%]`. *
+## `{perc_raw}`: Raw percentage, e.g. `10`. * `{title}`: Title of the
+## current web page. * `{title_sep}`: The string ` - ` if a title is set,
+## empty otherwise. * `{index}`: Index of this tab. * `{id}`: Internal
+## tab ID of this tab. * `{scroll_pos}`: Page scroll position. *
+## `{host}`: Host of the current web page. * `{backend}`: Either
+## ''webkit'' or ''webengine'' * `{private}`: Indicates when private mode
+## is enabled. * `{current_url}`: URL of the current web page. *
+## `{protocol}`: Protocol (http/https/...) of the current web page.
+## Type: FormatString
+# c.tabs.title.format = '{index}: {title}'
+
+## Format to use for the tab title for pinned tabs. The same placeholders
+## like for `tabs.title.format` are defined.
+## Type: FormatString
+# c.tabs.title.format_pinned = '{index}'
+
+## Width (in pixels or as percentage of the window) of the tab bar if
+## it's vertical.
+## Type: PercOrInt
+# c.tabs.width = '20%'
+
+## Wrap when changing tabs.
+## Type: Bool
+# c.tabs.wrap = True
+
+## What search to start when something else than a URL is entered.
+## Type: String
+## Valid values:
+## - naive: Use simple/naive check.
+## - dns: Use DNS requests (might be slow!).
+## - never: Never search automatically.
+# c.url.auto_search = 'naive'
+
+## Page to open if :open -t/-b/-w is used without URL. Use `about:blank`
+## for a blank page.
+## Type: FuzzyUrl
+# c.url.default_page = 'https://start.duckduckgo.com/'
+
+## URL segments where `:navigate increment/decrement` will search for a
+## number.
+## Type: FlagList
+## Valid values:
+## - host
+## - port
+## - path
+## - query
+## - anchor
+# c.url.incdec_segments = ['path', 'query']
+
+## Search engines which can be used via the address bar. Maps a search
+## engine name (such as `DEFAULT`, or `ddg`) to a URL with a `{}`
+## placeholder. The placeholder will be replaced by the search term, use
+## `{{` and `}}` for literal `{`/`}` signs. The search engine named
+## `DEFAULT` is used when `url.auto_search` is turned on and something
+## else than a URL was entered to be opened. Other search engines can be
+## used by prepending the search engine name to the search term, e.g.
+## `:open google qutebrowser`.
+## Type: Dict
+
+c.url.searchengines = { 'DEFAULT':'https://duckduckgo.com/?q={}', 'aw':'https://wiki.archlinux.org/?q={}', 'g':'https://www.google.com/?q={}'}
+
+## Page(s) to open at the start.
+## Type: List of FuzzyUrl, or FuzzyUrl
+# c.url.start_pages = ['https://start.duckduckgo.com']
+
+## URL parameters to strip with `:yank url`.
+## Type: List of String
+# c.url.yank_ignored_parameters = ['ref', 'utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content']
+
+## Hide the window decoration when using wayland.
+## Type: Bool
+# c.window.hide_wayland_decoration = False
+
+## Format to use for the window title. The same placeholders like for
+## `tabs.title.format` are defined.
+## Type: FormatString
+# c.window.title_format = '{perc}{title}{title_sep}qutebrowser'
+
+## Default zoom level.
+## Type: Perc
+# c.zoom.default = '100%'
+
+## Available zoom levels.
+## Type: List of Perc
+# c.zoom.levels = ['25%', '33%', '50%', '67%', '75%', '90%', '100%', '110%', '125%', '150%', '175%', '200%', '250%', '300%', '400%', '500%']
+
+## Number of zoom increments to divide the mouse wheel movements to.
+## Type: Int
+# c.zoom.mouse_divider = 512
+
+## Apply the zoom factor on a frame only to the text or to all content.
+## Type: Bool
+# c.zoom.text_only = False
+
+## Bindings for normal mode
+config.bind(',c', 'hint links spawn mpv --ytdl --ytdl-raw-options=username="v.graffagnino@btinternet.com",password="N9\Ch0n\H@n9" --cookies-file=~/.local/share/qutebrowser/webengine/Cookies --ytdl-raw-options=username=v\.graffagnino\@btinternet\.com,password=N9\\Ch0n\\H\@n9 {hint-url}')
+config.bind(',m', 'hint links spawn mpv --ytdl {hint-url} --load-unsafe-playlists')
+config.bind('m', 'hint links spawn mpv --ytdl -slang=en {hint-url} --load-unsafe-playlists')
+#config.bind('m', 'spawn umpv {url}')
+config.bind(',M', 'hint links spawn umpv {hint-url}')
+config.bind(',t', 'hint links spawn transmission-remote --add {hint-url}')
+config.bind(',y', 'hint links spawn /usr/bin/ts youtube-dl {hint-url}')
+config.bind(',Y', 'hint links spawn /usr/bin/urxvt -e mpsyt dlurl {hint-url}')
+config.bind(',q', 'hint links spawn echo "mpsyt dlurl {hint-url}" >> ~/ytdl.list')
+# config.bind("'", 'enter-mode jump_mark')
+# config.bind('+', 'zoom-in')
+# config.bind('-', 'zoom-out')
+# config.bind('.', 'repeat-command')
+# config.bind('/', 'set-cmd-text /')
+# config.bind(':', 'set-cmd-text :')
+# config.bind(';I', 'hint images tab')
+# config.bind(';O', 'hint links fill :open -t -r {hint-url}')
+# config.bind(';R', 'hint --rapid links window')
+# config.bind(';Y', 'hint links yank-primary')
+# config.bind(';b', 'hint all tab-bg')
+# config.bind(';d', 'hint links download')
+# config.bind(';f', 'hint all tab-fg')
+# config.bind(';h', 'hint all hover')
+# config.bind(';i', 'hint images')
+# config.bind(';o', 'hint links fill :open {hint-url}')
+# config.bind(';r', 'hint --rapid links tab-bg')
+# config.bind(';t', 'hint inputs')
+# config.bind(';y', 'hint links yank')
+# config.bind('<Alt-1>', 'tab-focus 1')
+# config.bind('<Alt-2>', 'tab-focus 2')
+# config.bind('<Alt-3>', 'tab-focus 3')
+# config.bind('<Alt-4>', 'tab-focus 4')
+# config.bind('<Alt-5>', 'tab-focus 5')
+# config.bind('<Alt-6>', 'tab-focus 6')
+# config.bind('<Alt-7>', 'tab-focus 7')
+# config.bind('<Alt-8>', 'tab-focus 8')
+# config.bind('<Alt-9>', 'tab-focus -1')
+# config.bind('<Ctrl-A>', 'navigate increment')
+# config.bind('<Ctrl-Alt-p>', 'print')
+# config.bind('<Ctrl-B>', 'scroll-page 0 -1')
+# config.bind('<Ctrl-D>', 'scroll-page 0 0.5')
+# config.bind('<Ctrl-F5>', 'reload -f')
+# config.bind('<Ctrl-F>', 'scroll-page 0 1')
+# config.bind('<Ctrl-N>', 'open -w')
+# config.bind('<Ctrl-PgDown>', 'tab-next')
+# config.bind('<Ctrl-PgUp>', 'tab-prev')
+# config.bind('<Ctrl-Q>', 'quit')
+# config.bind('<Ctrl-Return>', 'follow-selected -t')
+# config.bind('<Ctrl-Shift-N>', 'open -p')
+# config.bind('<Ctrl-Shift-T>', 'undo')
+# config.bind('<Ctrl-Shift-W>', 'close')
+# config.bind('<Ctrl-T>', 'open -t')
+# config.bind('<Ctrl-Tab>', 'tab-focus last')
+# config.bind('<Ctrl-U>', 'scroll-page 0 -0.5')
+# config.bind('<Ctrl-V>', 'enter-mode passthrough')
+# config.bind('<Ctrl-W>', 'tab-close')
+# config.bind('<Ctrl-X>', 'navigate decrement')
+# config.bind('<Ctrl-^>', 'tab-focus last')
+# config.bind('<Ctrl-h>', 'home')
+# config.bind('<Ctrl-p>', 'tab-pin')
+# config.bind('<Ctrl-s>', 'stop')
+# config.bind('<Escape>', 'clear-keychain ;; search ;; fullscreen --leave')
+# config.bind('<F11>', 'fullscreen')
+# config.bind('<F5>', 'reload')
+# config.bind('<Return>', 'follow-selected')
+# config.bind('<back>', 'back')
+# config.bind('<forward>', 'forward')
+# config.bind('=', 'zoom')
+# config.bind('?', 'set-cmd-text ?')
+# config.bind('@', 'run-macro')
+# config.bind('B', 'set-cmd-text -s :quickmark-load -t')
+# config.bind('D', 'tab-close -o')
+# config.bind('F', 'hint all tab')
+# config.bind('G', 'scroll-to-perc')
+# config.bind('H', 'back')
+config.bind('J', 'tab-prev')
+config.bind('K', 'tab-next')
+# config.bind('L', 'forward')
+# config.bind('M', 'bookmark-add')
+# config.bind('N', 'search-prev')
+# config.bind('O', 'set-cmd-text -s :open -t')
+# config.bind('PP', 'open -t -- {primary}')
+# config.bind('Pp', 'open -t -- {clipboard}')
+# config.bind('R', 'reload -f')
+# config.bind('Sb', 'open qute://bookmarks#bookmarks')
+# config.bind('Sh', 'open qute://history')
+# config.bind('Sq', 'open qute://bookmarks')
+# config.bind('Ss', 'open qute://settings')
+# config.bind('T', 'tab-focus')
+# config.bind('ZQ', 'quit')
+# config.bind('ZZ', 'quit --save')
+# config.bind('[[', 'navigate prev')
+# config.bind(']]', 'navigate next')
+# config.bind('`', 'enter-mode set_mark')
+# config.bind('ad', 'download-cancel')
+# config.bind('b', 'set-cmd-text -s :quickmark-load')
+# config.bind('cd', 'download-clear')
+# config.bind('co', 'tab-only')
+# config.bind('d', 'tab-close')
+# config.bind('f', 'hint')
+# config.bind('g$', 'tab-focus -1')
+# config.bind('g0', 'tab-focus 1')
+# config.bind('gB', 'set-cmd-text -s :bookmark-load -t')
+# config.bind('gC', 'tab-clone')
+# config.bind('gO', 'set-cmd-text :open -t -r {url:pretty}')
+# config.bind('gU', 'navigate up -t')
+# config.bind('g^', 'tab-focus 1')
+# config.bind('ga', 'open -t')
+# config.bind('gb', 'set-cmd-text -s :bookmark-load')
+config.bind('gd', 'download')
+config.bind('gf', 'view-source')
+# config.bind('gg', 'scroll-to-perc 0')
+# config.bind('gl', 'tab-move -')
+# config.bind('gm', 'tab-move')
+# config.bind('go', 'set-cmd-text :open {url:pretty}')
+# config.bind('gr', 'tab-move +')
+# config.bind('gt', 'set-cmd-text -s :buffer')
+# config.bind('gu', 'navigate up')
+# config.bind('h', 'scroll left')
+# config.bind('i', 'enter-mode insert')
+# config.bind('j', 'scroll down')
+# config.bind('k', 'scroll up')
+# config.bind('l', 'scroll right')
+# config.bind('m', 'quickmark-save')
+# config.bind('n', 'search-next')
+# config.bind('o', 'set-cmd-text -s :open')
+# config.bind('pP', 'open -- {primary}')
+# config.bind('pp', 'open -- {clipboard}')
+# config.bind('q', 'record-macro')
+# config.bind('r', 'reload')
+# config.bind('sf', 'save')
+# config.bind('sk', 'set-cmd-text -s :bind')
+# config.bind('sl', 'set-cmd-text -s :set -t')
+# config.bind('ss', 'set-cmd-text -s :set')
+# config.bind('tPH', 'config-cycle -p -u *://*.{url:host}/* content.plugins ;; reload')
+# config.bind('tPh', 'config-cycle -p -u *://{url:host}/* content.plugins ;; reload')
+# config.bind('tPu', 'config-cycle -p -u {url} content.plugins ;; reload')
+config.bind('tSH', 'config-cycle -p -u *://*.{url:host}/* content.javascript.enabled ;; reload')
+config.bind('tSh', 'config-cycle -p -u *://{url:host}/* content.javascript.enabled ;; reload')
+config.bind('tSu', 'config-cycle -p -u {url} content.javascript.enabled ;; reload')
+# config.bind('th', 'back -t')
+# config.bind('tl', 'forward -t')
+# config.bind('tpH', 'config-cycle -p -t -u *://*.{url:host}/* content.plugins ;; reload')
+# config.bind('tph', 'config-cycle -p -t -u *://{url:host}/* content.plugins ;; reload')
+# config.bind('tpu', 'config-cycle -p -t -u {url} content.plugins ;; reload')
+# config.bind('tsH', 'config-cycle -p -t -u *://*.{url:host}/* content.javascript.enabled ;; reload')
+# config.bind('tsh', 'config-cycle -p -t -u *://{url:host}/* content.javascript.enabled ;; reload')
+# config.bind('tsu', 'config-cycle -p -t -u {url} content.javascript.enabled ;; reload')
+# config.bind('u', 'undo')
+# config.bind('v', 'enter-mode caret')
+# config.bind('wB', 'set-cmd-text -s :bookmark-load -w')
+# config.bind('wO', 'set-cmd-text :open -w {url:pretty}')
+# config.bind('wP', 'open -w -- {primary}')
+# config.bind('wb', 'set-cmd-text -s :quickmark-load -w')
+# config.bind('wf', 'hint all window')
+# config.bind('wh', 'back -w')
+# config.bind('wi', 'inspector')
+# config.bind('wl', 'forward -w')
+# config.bind('wo', 'set-cmd-text -s :open -w')
+# config.bind('wp', 'open -w -- {clipboard}')
+# config.bind('xO', 'set-cmd-text :open -b -r {url:pretty}')
+# config.bind('xo', 'set-cmd-text -s :open -b')
+# config.bind('yD', 'yank domain -s')
+# config.bind('yP', 'yank pretty-url -s')
+# config.bind('yT', 'yank title -s')
+# config.bind('yY', 'yank -s')
+# config.bind('yd', 'yank domain')
+# config.bind('yp', 'yank pretty-url')
+# config.bind('yt', 'yank title')
+# config.bind('yy', 'yank')
+# config.bind('{{', 'navigate prev -t')
+# config.bind('}}', 'navigate next -t')
+
+## Bindings for caret mode
+# config.bind('$', 'move-to-end-of-line', mode='caret')
+# config.bind('0', 'move-to-start-of-line', mode='caret')
+# config.bind('<Ctrl-Space>', 'drop-selection', mode='caret')
+# config.bind('<Escape>', 'leave-mode', mode='caret')
+# config.bind('<Return>', 'yank selection', mode='caret')
+# config.bind('<Space>', 'toggle-selection', mode='caret')
+# config.bind('G', 'move-to-end-of-document', mode='caret')
+# config.bind('H', 'scroll left', mode='caret')
+# config.bind('J', 'scroll down', mode='caret')
+# config.bind('K', 'scroll up', mode='caret')
+# config.bind('L', 'scroll right', mode='caret')
+# config.bind('Y', 'yank selection -s', mode='caret')
+# config.bind('[', 'move-to-start-of-prev-block', mode='caret')
+# config.bind(']', 'move-to-start-of-next-block', mode='caret')
+# config.bind('b', 'move-to-prev-word', mode='caret')
+# config.bind('c', 'enter-mode normal', mode='caret')
+# config.bind('e', 'move-to-end-of-word', mode='caret')
+# config.bind('gg', 'move-to-start-of-document', mode='caret')
+# config.bind('h', 'move-to-prev-char', mode='caret')
+# config.bind('j', 'move-to-next-line', mode='caret')
+# config.bind('k', 'move-to-prev-line', mode='caret')
+# config.bind('l', 'move-to-next-char', mode='caret')
+# config.bind('v', 'toggle-selection', mode='caret')
+# config.bind('w', 'move-to-next-word', mode='caret')
+# config.bind('y', 'yank selection', mode='caret')
+# config.bind('{', 'move-to-end-of-prev-block', mode='caret')
+# config.bind('}', 'move-to-end-of-next-block', mode='caret')
+
+## Bindings for command mode
+# config.bind('<Alt-B>', 'rl-backward-word', mode='command')
+# config.bind('<Alt-Backspace>', 'rl-backward-kill-word', mode='command')
+# config.bind('<Alt-D>', 'rl-kill-word', mode='command')
+# config.bind('<Alt-F>', 'rl-forward-word', mode='command')
+# config.bind('<Ctrl-?>', 'rl-delete-char', mode='command')
+# config.bind('<Ctrl-A>', 'rl-beginning-of-line', mode='command')
+# config.bind('<Ctrl-B>', 'rl-backward-char', mode='command')
+# config.bind('<Ctrl-C>', 'completion-item-yank', mode='command')
+# config.bind('<Ctrl-D>', 'completion-item-del', mode='command')
+# config.bind('<Ctrl-E>', 'rl-end-of-line', mode='command')
+# config.bind('<Ctrl-F>', 'rl-forward-char', mode='command')
+# config.bind('<Ctrl-H>', 'rl-backward-delete-char', mode='command')
+# config.bind('<Ctrl-K>', 'rl-kill-line', mode='command')
+# config.bind('<Ctrl-N>', 'command-history-next', mode='command')
+# config.bind('<Ctrl-P>', 'command-history-prev', mode='command')
+# config.bind('<Ctrl-Return>', 'command-accept --rapid', mode='command')
+# config.bind('<Ctrl-Shift-C>', 'completion-item-yank --sel', mode='command')
+# config.bind('<Ctrl-Shift-Tab>', 'completion-item-focus prev-category', mode='command')
+# config.bind('<Ctrl-Tab>', 'completion-item-focus next-category', mode='command')
+# config.bind('<Ctrl-U>', 'rl-unix-line-discard', mode='command')
+# config.bind('<Ctrl-W>', 'rl-unix-word-rubout', mode='command')
+# config.bind('<Ctrl-Y>', 'rl-yank', mode='command')
+# config.bind('<Down>', 'completion-item-focus --history next', mode='command')
+# config.bind('<Escape>', 'leave-mode', mode='command')
+# config.bind('<Return>', 'command-accept', mode='command')
+# config.bind('<Shift-Delete>', 'completion-item-del', mode='command')
+# config.bind('<Shift-Tab>', 'completion-item-focus prev', mode='command')
+# config.bind('<Tab>', 'completion-item-focus next', mode='command')
+# config.bind('<Up>', 'completion-item-focus --history prev', mode='command')
+
+## Bindings for hint mode
+# config.bind('<Ctrl-B>', 'hint all tab-bg', mode='hint')
+# config.bind('<Ctrl-F>', 'hint links', mode='hint')
+# config.bind('<Ctrl-R>', 'hint --rapid links tab-bg', mode='hint')
+# config.bind('<Escape>', 'leave-mode', mode='hint')
+# config.bind('<Return>', 'follow-hint', mode='hint')
+
+## Bindings for insert mode
+# config.bind('<Ctrl-E>', 'open-editor', mode='insert')
+# config.bind('<Escape>', 'leave-mode', mode='insert')
+# config.bind('<Shift-Ins>', 'insert-text {primary}', mode='insert')
+config.bind('<Ctrl-i>', 'spawn --userscript qute-keepass -p ~/Documents/20151120.kdbx', mode='insert')
+
+## Bindings for passthrough mode
+# config.bind('<Ctrl-V>', 'leave-mode', mode='passthrough')
+
+## Bindings for prompt mode
+# config.bind('<Alt-B>', 'rl-backward-word', mode='prompt')
+# config.bind('<Alt-Backspace>', 'rl-backward-kill-word', mode='prompt')
+# config.bind('<Alt-D>', 'rl-kill-word', mode='prompt')
+# config.bind('<Alt-F>', 'rl-forward-word', mode='prompt')
+# config.bind('<Alt-Shift-Y>', 'prompt-yank --sel', mode='prompt')
+# config.bind('<Alt-Y>', 'prompt-yank', mode='prompt')
+# config.bind('<Ctrl-?>', 'rl-delete-char', mode='prompt')
+# config.bind('<Ctrl-A>', 'rl-beginning-of-line', mode='prompt')
+# config.bind('<Ctrl-B>', 'rl-backward-char', mode='prompt')
+# config.bind('<Ctrl-E>', 'rl-end-of-line', mode='prompt')
+# config.bind('<Ctrl-F>', 'rl-forward-char', mode='prompt')
+# config.bind('<Ctrl-H>', 'rl-backward-delete-char', mode='prompt')
+# config.bind('<Ctrl-K>', 'rl-kill-line', mode='prompt')
+# config.bind('<Ctrl-U>', 'rl-unix-line-discard', mode='prompt')
+# config.bind('<Ctrl-W>', 'rl-unix-word-rubout', mode='prompt')
+# config.bind('<Ctrl-X>', 'prompt-open-download', mode='prompt')
+# config.bind('<Ctrl-Y>', 'rl-yank', mode='prompt')
+# config.bind('<Down>', 'prompt-item-focus next', mode='prompt')
+# config.bind('<Escape>', 'leave-mode', mode='prompt')
+# config.bind('<Return>', 'prompt-accept', mode='prompt')
+# config.bind('<Shift-Tab>', 'prompt-item-focus prev', mode='prompt')
+# config.bind('<Tab>', 'prompt-item-focus next', mode='prompt')
+# config.bind('<Up>', 'prompt-item-focus prev', mode='prompt')
+
+## Bindings for register mode
+# config.bind('<Escape>', 'leave-mode', mode='register')
+
+## Bindings for yesno mode
+# config.bind('<Alt-Shift-Y>', 'prompt-yank --sel', mode='yesno')
+# config.bind('<Alt-Y>', 'prompt-yank', mode='yesno')
+# config.bind('<Escape>', 'leave-mode', mode='yesno')
+# config.bind('<Return>', 'prompt-accept', mode='yesno')
+# config.bind('n', 'prompt-accept no', mode='yesno')
+# config.bind('y', 'prompt-accept yes', mode='yesno')
+config.source('nord-qutebrowser.py')
diff --git a/.config/qutebrowser/jupyter-profile/config/bookmarks/urls b/.config/qutebrowser/jupyter-profile/config/bookmarks/urls
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.config/qutebrowser/jupyter-profile/config/bookmarks/urls
diff --git a/.config/qutebrowser/jupyter-profile/config/quickmarks b/.config/qutebrowser/jupyter-profile/config/quickmarks
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/.config/qutebrowser/jupyter-profile/config/quickmarks
diff --git a/.config/qutebrowser/jupyter-profile/keys.conf b/.config/qutebrowser/jupyter-profile/keys.conf
new file mode 100755
index 0000000..06fc600
--- /dev/null
+++ b/.config/qutebrowser/jupyter-profile/keys.conf
@@ -0,0 +1,709 @@
+# vim: ft=conf
+#
+# In this config file, qutebrowser's key bindings are configured.
+# The format looks like this:
+#
+# [keymode]
+#
+# command
+# keychain
+# keychain2
+# ...
+#
+# All blank lines and lines starting with '#' are ignored.
+# Inline-comments are not permitted.
+#
+# keymode is a comma separated list of modes in which the key binding should be
+# active. If keymode starts with !, the key binding is active in all modes
+# except the listed modes.
+#
+# For special keys (can't be part of a keychain), enclose them in `<`...`>`.
+# For modifiers, you can use either `-` or `+` as delimiters, and these names:
+#
+# * Control: `Control`, `Ctrl`
+# * Meta: `Meta`, `Windows`, `Mod4`
+# * Alt: `Alt`, `Mod1`
+# * Shift: `Shift`
+#
+# For simple keys (no `<>`-signs), a capital letter means the key is pressed
+# with Shift. For special keys (with `<>`-signs), you need to explicitly add
+# `Shift-` to match a key pressed with shift.
+#
+# Note that default keybindings are always bound, and need to be explicitly
+# unbound if you wish to remove them:
+#
+# <unbound>
+# keychain
+# keychain2
+# ...
+
+[!normal]
+
+leave-mode
+ <escape>
+ <ctrl-[>
+
+[normal]
+# Keybindings for normal mode.
+
+clear-keychain ;; search ;; fullscreen --leave
+ <escape>
+ <ctrl-[>
+
+set-cmd-text -s :open
+ o
+
+set-cmd-text :open {url:pretty}
+ go
+
+set-cmd-text -s :open -t
+ O
+
+set-cmd-text :open -t -i {url:pretty}
+ gO
+
+set-cmd-text -s :open -b
+ xo
+
+set-cmd-text :open -b -i {url:pretty}
+ xO
+
+set-cmd-text -s :open -w
+ wo
+
+set-cmd-text :open -w {url:pretty}
+ wO
+
+set-cmd-text /
+ /
+
+set-cmd-text ?
+ ?
+
+set-cmd-text :
+ :
+
+open -t
+ ga
+ <ctrl-t>
+
+open -w
+ <ctrl-n>
+
+tab-close
+ d
+ <ctrl-w>
+
+tab-close -o
+ D
+
+tab-only
+ co
+
+tab-focus
+ T
+
+tab-move
+ gm
+
+tab-move -
+ gl
+
+tab-move +
+ gr
+
+tab-next
+ J
+ <ctrl-pgdown>
+
+tab-prev
+ K
+ <ctrl-pgup>
+
+tab-clone
+ gC
+
+reload
+ r
+ <f5>
+
+reload -f
+ R
+ <ctrl-f5>
+
+back
+ H
+ <back>
+
+back -t
+ th
+
+back -w
+ wh
+
+forward
+ L
+ <forward>
+
+forward -t
+ tl
+
+forward -w
+ wl
+
+fullscreen
+ <f11>
+
+hint
+ f
+
+hint all tab
+ F
+
+hint all window
+ wf
+
+hint all tab-bg
+ ;b
+
+hint all tab-fg
+ ;f
+
+hint all hover
+ ;h
+
+hint images
+ ;i
+
+hint images tab
+ ;I
+
+hint links fill :open {hint-url}
+ ;o
+
+hint links fill :open -t -i {hint-url}
+ ;O
+
+hint links yank
+ ;y
+
+hint links yank-primary
+ ;Y
+
+hint --rapid links tab-bg
+ ;r
+
+hint --rapid links window
+ ;R
+
+hint links download
+ ;d
+
+hint inputs
+ ;t
+
+scroll left
+ h
+
+scroll down
+ j
+
+scroll up
+ k
+
+scroll right
+ l
+
+undo
+ u
+ <ctrl-shift-t>
+
+scroll-perc 0
+ gg
+
+scroll-perc
+ G
+
+search-next
+ n
+
+search-prev
+ N
+
+enter-mode insert
+ i
+
+enter-mode caret
+ v
+
+enter-mode set_mark
+ `
+
+enter-mode jump_mark
+ '
+
+yank
+ yy
+
+yank -s
+ yY
+
+yank title
+ yt
+
+yank title -s
+ yT
+
+yank domain
+ yd
+
+yank domain -s
+ yD
+
+yank pretty-url
+ yp
+
+yank pretty-url -s
+ yP
+
+open -- {clipboard}
+ pp
+
+open -- {primary}
+ pP
+
+open -t -- {clipboard}
+ Pp
+
+open -t -- {primary}
+ PP
+
+open -w -- {clipboard}
+ wp
+
+open -w -- {primary}
+ wP
+
+set-cmd-text -s :quickmark-load
+ b
+
+set-cmd-text -s :quickmark-load -t
+ B
+
+set-cmd-text -s :quickmark-load -w
+ wb
+
+set-cmd-text -s :bookmark-load
+ gb
+
+set-cmd-text -s :bookmark-load -t
+ gB
+
+set-cmd-text -s :bookmark-load -w
+ wB
+
+save
+ sf
+
+set-cmd-text -s :set
+ ss
+
+set-cmd-text -s :set -t
+ sl
+
+set-cmd-text -s :bind
+ sk
+
+zoom-out
+ -
+
+zoom-in
+ +
+
+zoom
+ =
+
+navigate prev
+ [[
+
+navigate next
+ ]]
+
+navigate prev -t
+ {{
+
+navigate next -t
+ }}
+
+navigate up
+ gu
+
+navigate up -t
+ gU
+
+navigate increment
+ <ctrl-a>
+
+navigate decrement
+ <ctrl-x>
+
+inspector
+ wi
+
+download
+ gd
+
+download-cancel
+ ad
+
+download-clear
+ cd
+
+view-source
+ gf
+
+set-cmd-text -s :buffer
+ gt
+
+tab-focus last
+ <ctrl-tab>
+
+enter-mode passthrough
+ <ctrl-v>
+
+quit
+ <ctrl-q>
+
+scroll-page 0 1
+ <ctrl-f>
+
+scroll-page 0 -1
+ <ctrl-b>
+
+scroll-page 0 0.5
+ <ctrl-d>
+
+scroll-page 0 -0.5
+ <ctrl-u>
+
+tab-focus 1
+ <alt-1>
+
+tab-focus 2
+ <alt-2>
+
+tab-focus 3
+ <alt-3>
+
+tab-focus 4
+ <alt-4>
+
+tab-focus 5
+ <alt-5>
+
+tab-focus 6
+ <alt-6>
+
+tab-focus 7
+ <alt-7>
+
+tab-focus 8
+ <alt-8>
+
+tab-focus 9
+ <alt-9>
+
+home
+ <ctrl-h>
+
+stop
+ <ctrl-s>
+
+print
+ <ctrl-alt-p>
+
+open qute:settings
+ Ss
+
+follow-selected
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+follow-selected -t
+ <ctrl-return>
+ <ctrl-enter>
+
+repeat-command
+ .
+
+record-macro
+ q
+
+run-macro
+ @
+
+spawn mpv {url}
+ mpv
+ m
+
+hint links spawn umpv {hint-urls}
+ MPV
+
+hint links spawn mpv {hint-url}
+ M
+
+spawn tsp-ytdl {hint-url}
+ ;M
+
+wq
+ ZZ
+
+tab-focus -1
+ g$
+
+tab-pin
+ <ctrl-p>
+
+[insert]
+# Keybindings for insert mode.
+# Since normal keypresses are passed through, only special keys are
+# supported in this mode.
+# Useful hidden commands to map in this section:
+# * `open-editor`: Open a texteditor with the focused field.
+# * `paste-primary`: Paste primary selection at cursor position.
+
+open-editor
+ <ctrl-e>
+
+insert-text {primary}
+ <shift-ins>
+
+[hint]
+# Keybindings for hint mode.
+# Since normal keypresses are passed through, only special keys are
+# supported in this mode.
+# Useful hidden commands to map in this section:
+# * `follow-hint`: Follow the currently selected hint.
+
+follow-hint
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+hint --rapid links tab-bg
+ <ctrl-r>
+
+hint links
+ <ctrl-f>
+
+hint all tab-bg
+ <ctrl-b>
+
+[command]
+# Keybindings for command mode.
+# Since normal keypresses are passed through, only special keys are
+# supported in this mode.
+# Useful hidden commands to map in this section:
+# * `command-history-prev`: Switch to previous command in history.
+# * `command-history-next`: Switch to next command in history.
+# * `completion-item-focus`: Select another item in completion.
+# * `command-accept`: Execute the command currently in the commandline.
+
+command-history-prev
+ <ctrl-p>
+
+command-history-next
+ <ctrl-n>
+
+completion-item-focus prev
+ <shift-tab>
+ <up>
+
+completion-item-focus next
+ <tab>
+ <down>
+
+completion-item-focus next-category
+ <ctrl-tab>
+
+completion-item-focus prev-category
+ <ctrl-shift-tab>
+
+completion-item-del
+ <ctrl-d>
+
+command-accept
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+[prompt]
+# Keybindings for prompts in the status line.
+# You can bind normal keys in this mode, but they will be only active
+# when a yes/no-prompt is asked. For other prompt modes, you can only
+# bind special keys.
+# Useful hidden commands to map in this section:
+# * `prompt-accept`: Confirm the entered value.
+# * `prompt-accept yes`: Answer yes to a yes/no question.
+# * `prompt-accept no`: Answer no to a yes/no question.
+
+prompt-accept
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+prompt-accept yes
+ y
+
+prompt-accept no
+ n
+
+prompt-open-download
+ <ctrl-x>
+
+prompt-item-focus prev
+ <shift-tab>
+ <up>
+
+prompt-item-focus next
+ <tab>
+ <down>
+
+[command,prompt]
+
+rl-backward-char
+ <ctrl-b>
+
+rl-forward-char
+ <ctrl-f>
+
+rl-backward-word
+ <alt-b>
+
+rl-forward-word
+ <alt-f>
+
+rl-beginning-of-line
+ <ctrl-a>
+
+rl-end-of-line
+ <ctrl-e>
+
+rl-unix-line-discard
+ <ctrl-u>
+
+rl-kill-line
+ <ctrl-k>
+
+rl-kill-word
+ <alt-d>
+
+rl-unix-word-rubout
+ <ctrl-w>
+
+rl-backward-kill-word
+ <alt-backspace>
+
+rl-yank
+ <ctrl-y>
+
+rl-delete-char
+ <ctrl-?>
+
+rl-backward-delete-char
+ <ctrl-h>
+
+[caret]
+
+toggle-selection
+ v
+ <space>
+
+drop-selection
+ <ctrl-space>
+
+enter-mode normal
+ c
+
+move-to-next-line
+ j
+
+move-to-prev-line
+ k
+
+move-to-next-char
+ l
+
+move-to-prev-char
+ h
+
+move-to-end-of-word
+ e
+
+move-to-next-word
+ w
+
+move-to-prev-word
+ b
+
+move-to-start-of-next-block
+ ]
+
+move-to-start-of-prev-block
+ [
+
+move-to-end-of-next-block
+ }
+
+move-to-end-of-prev-block
+ {
+
+move-to-start-of-line
+ 0
+
+move-to-end-of-line
+ $
+
+move-to-start-of-document
+ gg
+
+move-to-end-of-document
+ G
+
+yank selection -s
+ Y
+
+yank selection
+ y
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+scroll left
+ H
+
+scroll down
+ J
+
+scroll up
+ K
+
+scroll right
+ L
+
diff --git a/.config/qutebrowser/jupyter-profile/nord-qutebrowser.py b/.config/qutebrowser/jupyter-profile/nord-qutebrowser.py
new file mode 100644
index 0000000..9dd9064
--- /dev/null
+++ b/.config/qutebrowser/jupyter-profile/nord-qutebrowser.py
@@ -0,0 +1,336 @@
+nord = {
+ # Polar Night
+ 'nord0': '#2e3440',
+ 'nord1': '#3b4252',
+ 'nord2': '#434c5e',
+ 'nord3': '#4c566a',
+ # Snow Storm
+ 'nord4': '#d8dee9',
+ 'nord5': '#e5e9f0',
+ 'nord6': '#eceff4',
+ # Frost
+ 'nord7': '#8fbcbb',
+ 'nord8': '#88c0d0',
+ 'nord9': '#81a1c1',
+ 'nord10': '#5e81ac',
+ # Aurora
+ 'nord11': '#bf616a',
+ 'nord12': '#d08770',
+ 'nord13': '#ebcb8b',
+ 'nord14': '#a3be8c',
+ 'nord15': '#b48ead',
+}
+
+## Background color of the completion widget category headers.
+## Type: QssColor
+c.colors.completion.category.bg = nord['nord0']
+
+## Bottom border color of the completion widget category headers.
+## Type: QssColor
+c.colors.completion.category.border.bottom = nord['nord0']
+
+## Top border color of the completion widget category headers.
+## Type: QssColor
+c.colors.completion.category.border.top = nord['nord0']
+
+## Foreground color of completion widget category headers.
+## Type: QtColor
+c.colors.completion.category.fg = nord['nord5']
+
+## Background color of the completion widget for even rows.
+## Type: QssColor
+c.colors.completion.even.bg = nord['nord1']
+
+## Background color of the completion widget for odd rows.
+## Type: QssColor
+c.colors.completion.odd.bg = nord['nord1']
+
+## Text color of the completion widget.
+## Type: QtColor
+c.colors.completion.fg = nord['nord4']
+
+## Background color of the selected completion item.
+## Type: QssColor
+c.colors.completion.item.selected.bg = nord['nord3']
+
+## Bottom border color of the selected completion item.
+## Type: QssColor
+c.colors.completion.item.selected.border.bottom = nord['nord3']
+
+## Top border color of the completion widget category headers.
+## Type: QssColor
+c.colors.completion.item.selected.border.top = nord['nord3']
+
+## Foreground color of the selected completion item.
+## Type: QtColor
+c.colors.completion.item.selected.fg = nord['nord6']
+
+## Foreground color of the matched text in the completion.
+## Type: QssColor
+c.colors.completion.match.fg = nord['nord13']
+
+## Color of the scrollbar in completion view
+## Type: QssColor
+c.colors.completion.scrollbar.bg = nord['nord1']
+
+## Color of the scrollbar handle in completion view.
+## Type: QssColor
+c.colors.completion.scrollbar.fg = nord['nord5']
+
+## Background color for the download bar.
+## Type: QssColor
+c.colors.downloads.bar.bg = nord['nord0']
+
+## Background color for downloads with errors.
+## Type: QtColor
+c.colors.downloads.error.bg = nord['nord11']
+
+## Foreground color for downloads with errors.
+## Type: QtColor
+c.colors.downloads.error.fg = nord['nord5']
+
+## Color gradient stop for download backgrounds.
+## Type: QtColor
+c.colors.downloads.stop.bg = nord['nord15']
+
+## Color gradient interpolation system for download backgrounds.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+c.colors.downloads.system.bg = 'none'
+
+## Background color for hints. Note that you can use a `rgba(...)` value
+## for transparency.
+## Type: QssColor
+c.colors.hints.bg = nord['nord13']
+
+## Font color for hints.
+## Type: QssColor
+c.colors.hints.fg = nord['nord0']
+
+## Font color for the matched part of hints.
+## Type: QssColor
+c.colors.hints.match.fg = nord['nord10']
+
+## Background color of the keyhint widget.
+## Type: QssColor
+c.colors.keyhint.bg = nord['nord1']
+
+## Text color for the keyhint widget.
+## Type: QssColor
+c.colors.keyhint.fg = nord['nord5']
+
+## Highlight color for keys to complete the current keychain.
+## Type: QssColor
+c.colors.keyhint.suffix.fg = nord['nord13']
+
+## Background color of an error message.
+## Type: QssColor
+c.colors.messages.error.bg = nord['nord11']
+
+## Border color of an error message.
+## Type: QssColor
+c.colors.messages.error.border = nord['nord11']
+
+## Foreground color of an error message.
+## Type: QssColor
+c.colors.messages.error.fg = nord['nord5']
+
+## Background color of an info message.
+## Type: QssColor
+c.colors.messages.info.bg = nord['nord8']
+
+## Border color of an info message.
+## Type: QssColor
+c.colors.messages.info.border = nord['nord8']
+
+## Foreground color an info message.
+## Type: QssColor
+c.colors.messages.info.fg = nord['nord5']
+
+## Background color of a warning message.
+## Type: QssColor
+c.colors.messages.warning.bg = nord['nord12']
+
+## Border color of a warning message.
+## Type: QssColor
+c.colors.messages.warning.border = nord['nord12']
+
+## Foreground color a warning message.
+## Type: QssColor
+c.colors.messages.warning.fg = nord['nord5']
+
+## Background color for prompts.
+## Type: QssColor
+c.colors.prompts.bg = nord['nord2']
+
+# ## Border used around UI elements in prompts.
+# ## Type: String
+c.colors.prompts.border = '1px solid ' + nord['nord0']
+
+## Foreground color for prompts.
+## Type: QssColor
+c.colors.prompts.fg = nord['nord5']
+
+## Background color for the selected item in filename prompts.
+## Type: QssColor
+c.colors.prompts.selected.bg = nord['nord3']
+
+## Background color of the statusbar in caret mode.
+## Type: QssColor
+c.colors.statusbar.caret.bg = nord['nord15']
+
+## Foreground color of the statusbar in caret mode.
+## Type: QssColor
+c.colors.statusbar.caret.fg = nord['nord5']
+
+## Background color of the statusbar in caret mode with a selection.
+## Type: QssColor
+c.colors.statusbar.caret.selection.bg = nord['nord15']
+
+## Foreground color of the statusbar in caret mode with a selection.
+## Type: QssColor
+c.colors.statusbar.caret.selection.fg = nord['nord5']
+
+## Background color of the statusbar in command mode.
+## Type: QssColor
+c.colors.statusbar.command.bg = nord['nord2']
+
+## Foreground color of the statusbar in command mode.
+## Type: QssColor
+c.colors.statusbar.command.fg = nord['nord5']
+
+## Background color of the statusbar in private browsing + command mode.
+## Type: QssColor
+c.colors.statusbar.command.private.bg = nord['nord2']
+
+## Foreground color of the statusbar in private browsing + command mode.
+## Type: QssColor
+c.colors.statusbar.command.private.fg = nord['nord5']
+
+## Background color of the statusbar in insert mode.
+## Type: QssColor
+c.colors.statusbar.insert.bg = nord['nord14']
+
+## Foreground color of the statusbar in insert mode.
+## Type: QssColor
+c.colors.statusbar.insert.fg = nord['nord1']
+
+## Background color of the statusbar.
+## Type: QssColor
+c.colors.statusbar.normal.bg = nord['nord0']
+
+## Foreground color of the statusbar.
+## Type: QssColor
+c.colors.statusbar.normal.fg = nord['nord5']
+
+## Background color of the statusbar in passthrough mode.
+## Type: QssColor
+c.colors.statusbar.passthrough.bg = nord['nord10']
+
+## Foreground color of the statusbar in passthrough mode.
+## Type: QssColor
+c.colors.statusbar.passthrough.fg = nord['nord5']
+
+## Background color of the statusbar in private browsing mode.
+## Type: QssColor
+c.colors.statusbar.private.bg = nord['nord3']
+
+## Foreground color of the statusbar in private browsing mode.
+## Type: QssColor
+c.colors.statusbar.private.fg = nord['nord5']
+
+## Background color of the progress bar.
+## Type: QssColor
+c.colors.statusbar.progress.bg = nord['nord5']
+
+## Foreground color of the URL in the statusbar on error.
+## Type: QssColor
+c.colors.statusbar.url.error.fg = nord['nord11']
+
+## Default foreground color of the URL in the statusbar.
+## Type: QssColor
+c.colors.statusbar.url.fg = nord['nord5']
+
+## Foreground color of the URL in the statusbar for hovered links.
+## Type: QssColor
+c.colors.statusbar.url.hover.fg = nord['nord8']
+
+## Foreground color of the URL in the statusbar on successful load
+## (http).
+## Type: QssColor
+c.colors.statusbar.url.success.http.fg = nord['nord5']
+
+## Foreground color of the URL in the statusbar on successful load
+## (https).
+## Type: QssColor
+c.colors.statusbar.url.success.https.fg = nord['nord14']
+
+## Foreground color of the URL in the statusbar when there's a warning.
+## Type: QssColor
+c.colors.statusbar.url.warn.fg = nord['nord12']
+
+## Background color of the tab bar.
+## Type: QtColor
+c.colors.tabs.bar.bg = nord['nord3']
+
+## Background color of unselected even tabs.
+## Type: QtColor
+c.colors.tabs.even.bg = nord['nord3']
+
+## Foreground color of unselected even tabs.
+## Type: QtColor
+c.colors.tabs.even.fg = nord['nord5']
+
+## Color for the tab indicator on errors.
+## Type: QtColor
+c.colors.tabs.indicator.error = nord['nord11']
+
+## Color gradient start for the tab indicator.
+## Type: QtColor
+# c.colors.tabs.indicator.start = nord['violet']
+
+## Color gradient end for the tab indicator.
+## Type: QtColor
+# c.colors.tabs.indicator.stop = nord['orange']
+
+## Color gradient interpolation system for the tab indicator.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+c.colors.tabs.indicator.system = 'none'
+
+## Background color of unselected odd tabs.
+## Type: QtColor
+c.colors.tabs.odd.bg = nord['nord3']
+
+## Foreground color of unselected odd tabs.
+## Type: QtColor
+c.colors.tabs.odd.fg = nord['nord5']
+
+# ## Background color of selected even tabs.
+# ## Type: QtColor
+c.colors.tabs.selected.even.bg = nord['nord0']
+
+# ## Foreground color of selected even tabs.
+# ## Type: QtColor
+c.colors.tabs.selected.even.fg = nord['nord5']
+
+# ## Background color of selected odd tabs.
+# ## Type: QtColor
+c.colors.tabs.selected.odd.bg = nord['nord0']
+
+# ## Foreground color of selected odd tabs.
+# ## Type: QtColor
+c.colors.tabs.selected.odd.fg = nord['nord5']
+
+## Background color for webpages if unset (or empty to use the theme's
+## color)
+## Type: QtColor
+# c.colors.webpage.bg = 'white'
diff --git a/.config/qutebrowser/jupyter-profile/qutebrowser.conf b/.config/qutebrowser/jupyter-profile/qutebrowser.conf
new file mode 100755
index 0000000..3fc85a8
--- /dev/null
+++ b/.config/qutebrowser/jupyter-profile/qutebrowser.conf
@@ -0,0 +1,1612 @@
+# vim: ft=dosini
+
+# Configfile for qutebrowser.
+#
+# This configfile is parsed by python's configparser in extended
+# interpolation mode. The format is very INI-like, so there are
+# categories like [general] with "key = value"-pairs.
+#
+# Note that you shouldn't add your own comments, as this file is
+# regenerated every time the config is saved.
+#
+# Interpolation looks like ${value} or ${section:value} and will be
+# replaced by the respective value.
+#
+# Some settings will expand environment variables. Note that, since
+# interpolation is run first, you will need to escape the $ char as
+# described below.
+#
+# This is the default config, so if you want to remove anything from
+# here (as opposed to change/add), for example a key binding, set it to
+# an empty value.
+#
+# You will need to escape the following values:
+# - # at the start of the line (at the first position of the key) (\#)
+# - $ in a value ($$)
+
+
+# General/miscellaneous options.
+[general]
+
+# ignore-case (IgnoreCase):
+# Whether to find text on a page case-insensitively.
+# true: Search case-insensitively
+# false: Search case-sensitively
+# smart: Search case-sensitively if there are capital chars
+# Default: smart
+ignore-case = smart
+
+# startpage (List of String):
+# The default page(s) to open at the start, separated by commas.
+# Default: https://start.duckduckgo.com
+startpage = https://start.duckduckgo.com
+
+# yank-ignored-url-parameters (List of String):
+# The URL parameters to strip with :yank url, separated by commas.
+# Default:
+# ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content
+yank-ignored-url-parameters = ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content
+
+# default-open-dispatcher (String):
+# The default program used to open downloads. Set to an empty string
+# to use the default internal handler.
+# Any {} in the string will be expanded to the filename, else the
+# filename will be appended.
+# Default:
+default-open-dispatcher =
+
+# default-page (FuzzyUrl):
+# The page to open if :open -t/-b/-w is used without URL. Use
+# `about:blank` for a blank page.
+# Default: ${startpage}
+default-page = ${startpage}
+
+# auto-search (AutoSearch):
+# Whether to start a search when something else than a URL is
+# entered.
+# naive: Use simple/naive check.
+# dns: Use DNS requests (might be slow!).
+# false: Never search automatically.
+# Default: naive
+auto-search = naive
+
+# auto-save-config (Bool):
+# Whether to save the config automatically on quit.
+# Valid values: true, false
+# Default: true
+auto-save-config = true
+
+# auto-save-interval (Int):
+# How often (in milliseconds) to auto-save config/cookies/etc.
+# Default: 15000
+auto-save-interval = 15000
+
+# editor (ShellCommand):
+# The editor (and arguments) to use for the `open-editor` command.
+# The arguments get split like in a shell, so you can use `"` or `'`
+# to quote them.
+# `{}` gets replaced by the filename of the file to be edited.
+# Default: gvim -f "{}"
+editor = gvim -f "{}"
+
+# editor-encoding (Encoding):
+# Encoding to use for editor.
+# Default: utf-8
+editor-encoding = utf-8
+
+# private-browsing (Bool):
+# Open new windows in private browsing mode which does not record
+# visited pages.
+# Valid values: true, false
+# Default: false
+private-browsing = true
+
+# developer-extras (Bool):
+# Enable extra tools for Web developers.
+# This needs to be enabled for `:inspector` to work and also adds an
+# _Inspect_ entry to the context menu. For QtWebEngine, see
+# 'qutebrowser --help' instead.
+# Valid values: true, false
+# Default: false
+developer-extras = true
+
+# print-element-backgrounds (Bool):
+# Whether the background color and images are also drawn when the
+# page is printed.
+# This setting only works with Qt 5.8 or newer when using the
+# QtWebEngine backend.
+# Valid values: true, false
+# Default: true
+print-element-backgrounds = true
+
+# xss-auditing (Bool):
+# Whether load requests should be monitored for cross-site scripting
+# attempts.
+# Suspicious scripts will be blocked and reported in the inspector's
+# JavaScript console. Enabling this feature might have an impact on
+# performance.
+# Valid values: true, false
+# Default: false
+xss-auditing = false
+
+# default-encoding (String):
+# Default encoding to use for websites.
+# The encoding must be a string describing an encoding such as
+# _utf-8_, _iso-8859-1_, etc.
+# Default: iso-8859-1
+default-encoding = _utf-8_
+
+# new-instance-open-target (String):
+# How to open links in an existing instance if a new one is
+# launched.
+# tab: Open a new tab in the existing window and activate the
+# window.
+# tab-bg: Open a new background tab in the existing window and
+# activate the window.
+# tab-silent: Open a new tab in the existing window without
+# activating the window.
+# tab-bg-silent: Open a new background tab in the existing
+# window without activating the window.
+# window: Open in a new window.
+# Default: tab
+new-instance-open-target = tab
+
+# new-instance-open-target.window (String):
+# Which window to choose when opening links as new tabs.
+# first-opened: Open new tabs in the first (oldest) opened
+# window.
+# last-opened: Open new tabs in the last (newest) opened window.
+# last-focused: Open new tabs in the most recently focused
+# window.
+# last-visible: Open new tabs in the most recently visible
+# window.
+# Default: last-focused
+new-instance-open-target.window = last-focused
+
+# log-javascript-console (String):
+# How to log javascript console messages.
+# none: Don't log messages.
+# debug: Log messages with debug level.
+# info: Log messages with info level.
+# Default: debug
+log-javascript-console = debug
+
+# save-session (Bool):
+# Whether to always save the open pages.
+# Valid values: true, false
+# Default: false
+save-session = true
+
+# session-default-name (SessionName):
+# The name of the session to save by default, or empty for the last
+# loaded session.
+# Default:
+session-default-name =
+
+# url-incdec-segments (FlagList):
+# The URL segments where `:navigate increment/decrement` will search
+# for a number.
+# Valid values: host, path, query, anchor
+# Default: path,query
+url-incdec-segments = path,query
+
+
+# General options related to the user interface.
+[ui]
+
+# history-session-interval (Int):
+# The maximum time in minutes between two history items for them to
+# be considered being from the same session. Use -1 to disable
+# separation.
+# Default: 30
+history-session-interval = 30
+
+# zoom-levels (List of Perc):
+# The available zoom levels, separated by commas.
+# Default:
+# 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500%
+zoom-levels = 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500%
+
+# default-zoom (Perc):
+# The default zoom level.
+# Default: 100%
+default-zoom = 100%
+
+# downloads-position (VerticalPosition):
+# Where to show the downloaded files.
+# Valid values: top, bottom
+# Default: top
+downloads-position = top
+
+# status-position (VerticalPosition):
+# The position of the status bar.
+# Valid values: top, bottom
+# Default: bottom
+status-position = bottom
+
+# message-timeout (Int):
+# Time (in ms) to show messages in the statusbar for.
+# Set to 0 to never clear messages.
+# Default: 2000
+message-timeout = 2000
+
+# message-unfocused (Bool):
+# Whether to show messages in unfocused windows.
+# Valid values: true, false
+# Default: false
+message-unfocused = false
+
+# confirm-quit (ConfirmQuit):
+# Whether to confirm quitting the application.
+# always: Always show a confirmation.
+# multiple-tabs: Show a confirmation if multiple tabs are
+# opened.
+# downloads: Show a confirmation if downloads are running
+# never: Never show a confirmation.
+# Default: never
+confirm-quit = never
+
+# zoom-text-only (Bool):
+# Whether the zoom factor on a frame applies only to the text or to
+# all content.
+# Valid values: true, false
+# Default: false
+zoom-text-only = false
+
+# frame-flattening (Bool):
+# Whether to expand each subframe to its contents.
+# This will flatten all the frames to become one scrollable page.
+# Valid values: true, false
+# Default: false
+frame-flattening = false
+
+# user-stylesheet (File):
+# User stylesheet to use (absolute filename or filename relative to
+# the config directory). Will expand environment variables.
+# Default:
+user-stylesheet =
+
+# hide-scrollbar (Bool):
+# Hide the main scrollbar.
+# Valid values: true, false
+# Default: true
+hide-scrollbar = true
+
+# smooth-scrolling (Bool):
+# Whether to enable smooth scrolling for web pages. Note smooth
+# scrolling does not work with the :scroll-px command.
+# Valid values: true, false
+# Default: false
+smooth-scrolling = false
+
+# remove-finished-downloads (Int):
+# Number of milliseconds to wait before removing finished downloads.
+# Will not be removed if value is -1.
+# Default: -1
+remove-finished-downloads = -1
+
+# hide-statusbar (Bool):
+# Whether to hide the statusbar unless a message is shown.
+# Valid values: true, false
+# Default: false
+hide-statusbar = false
+
+# statusbar-padding (Padding):
+# Padding for statusbar (top, bottom, left, right).
+# Default: 1,1,0,0
+statusbar-padding = 1,1,0,0
+
+# window-title-format (FormatString):
+# The format to use for the window title. The following placeholders
+# are defined:
+# * `{perc}`: The percentage as a string like `[10%]`.
+# * `{perc_raw}`: The raw percentage, e.g. `10`
+# * `{title}`: The title of the current web page
+# * `{title_sep}`: The string ` - ` if a title is set, empty
+# otherwise.
+# * `{id}`: The internal window ID of this window.
+# * `{scroll_pos}`: The page scroll position.
+# * `{host}`: The host of the current web page.
+# * `{backend}`: Either 'webkit' or 'webengine'
+# * `{private}` : Indicates when private mode is enabled.
+# Default: {perc}{title}{title_sep}qutebrowser
+window-title-format = {perc}{title}{title_sep}qutebrowser
+
+# modal-js-dialog (Bool):
+# Use standard JavaScript modal dialog for alert() and confirm()
+# Valid values: true, false
+# Default: false
+modal-js-dialog = false
+
+# hide-wayland-decoration (Bool):
+# Hide the window decoration when using wayland (requires restart)
+# Valid values: true, false
+# Default: false
+hide-wayland-decoration = false
+
+# keyhint-blacklist (List of String):
+# Keychains that shouldn't be shown in the keyhint dialog
+# Globs are supported, so ';*' will blacklist all keychainsstarting
+# with ';'. Use '*' to disable keyhints
+# Default:
+keyhint-blacklist =
+
+# keyhint-delay (Int):
+# Time from pressing a key to seeing the keyhint dialog (ms)
+# Default: 500
+keyhint-delay = 500
+
+# prompt-radius (Int):
+# The rounding radius for the edges of prompts.
+# Default: 8
+prompt-radius = 8
+
+# prompt-filebrowser (Bool):
+# Show a filebrowser in upload/download prompts.
+# Valid values: true, false
+# Default: true
+prompt-filebrowser = true
+
+
+# Settings related to the network.
+[network]
+
+# do-not-track (Bool):
+# Value to send in the `DNT` header.
+# Valid values: true, false
+# Default: true
+do-not-track = true
+
+# accept-language (String):
+# Value to send in the `accept-language` header.
+# Default: en-US,en
+accept-language = en-US,en;q=0.5
+
+# referer-header (String):
+# Send the Referer header
+# always: Always send.
+# never: Never send; this is not recommended, as some sites may
+# break.
+# same-domain: Only send for the same domain. This will still
+# protect your privacy, but shouldn't break any sites.
+# Default: same-domain
+referer-header = same-domain
+
+# user-agent (UserAgent):
+# User agent to send. Empty to send the default.
+# Default:
+user-agent = Mozilla/5.0
+
+# proxy (Proxy):
+# The proxy to use.
+# In addition to the listed values, you can use a `socks://...` or
+# `http://...` URL.
+# system: Use the system wide proxy.
+# none: Don't use any proxy
+# Default: system
+proxy = system
+
+# proxy-dns-requests (Bool):
+# Whether to send DNS requests over the configured proxy.
+# Valid values: true, false
+# Default: true
+proxy-dns-requests = true
+
+# ssl-strict (BoolAsk):
+# Whether to validate SSL handshakes.
+# Valid values: true, false, ask
+# Default: ask
+ssl-strict = ask
+
+# dns-prefetch (Bool):
+# Whether to try to pre-fetch DNS entries to speed up browsing.
+# Valid values: true, false
+# Default: true
+dns-prefetch = true
+
+# custom-headers (HeaderDict):
+# Set custom headers for qutebrowser HTTP requests.
+# Default:
+custom-headers =
+
+# netrc-file (File):
+# Set location of a netrc-file for HTTP authentication. If empty,
+# ~/.netrc is used.
+# Default:
+netrc-file =
+
+
+# Options related to completion and command history.
+[completion]
+
+# show (String):
+# When to show the autocompletion window.
+# always: Whenever a completion is available.
+# auto: Whenever a completion is requested.
+# never: Never.
+# Default: always
+show = always
+
+# download-path-suggestion (String):
+# What to display in the download filename input.
+# path: Show only the download path.
+# filename: Show only download filename.
+# both: Show download path and filename.
+# Default: path
+download-path-suggestion = path
+
+# timestamp-format (TimestampTemplate):
+# How to format timestamps (e.g. for history)
+# Default: %Y-%m-%d
+timestamp-format = %Y-%m-%d
+
+# height (PercOrInt):
+# The height of the completion, in px or as percentage of the
+# window.
+# Default: 50%
+height = 50%
+
+# cmd-history-max-items (Int):
+# How many commands to save in the command history.
+# 0: no history / -1: unlimited
+# Default: 100
+cmd-history-max-items = 100
+
+# web-history-max-items (Int):
+# How many URLs to show in the web history.
+# 0: no history / -1: unlimited
+# Default: 1000
+web-history-max-items = 1000
+
+# quick-complete (Bool):
+# Whether to move on to the next part when there's only one possible
+# completion left.
+# Valid values: true, false
+# Default: true
+quick-complete = true
+
+# shrink (Bool):
+# Whether to shrink the completion to be smaller than the configured
+# size if there are no scrollbars.
+# Valid values: true, false
+# Default: false
+shrink = false
+
+# scrollbar-width (Int):
+# Width of the scrollbar in the completion window (in px).
+# Default: 12
+scrollbar-width = 12
+
+# scrollbar-padding (Int):
+# Padding of scrollbar handle in completion window (in px).
+# Default: 2
+scrollbar-padding = 2
+
+
+# Options related to input modes.
+[input]
+
+# timeout (Int):
+# Timeout (in milliseconds) for ambiguous key bindings.
+# If the current input forms both a complete match and a partial
+# match, the complete match will be executed after this time.
+# Default: 500
+timeout = 500
+
+# partial-timeout (Int):
+# Timeout (in milliseconds) for partially typed key bindings.
+# If the current input forms only partial matches, the keystring
+# will be cleared after this time.
+# Default: 5000
+partial-timeout = 5000
+
+# insert-mode-on-plugins (Bool):
+# Whether to switch to insert mode when clicking flash and other
+# plugins.
+# Valid values: true, false
+# Default: false
+insert-mode-on-plugins = false
+
+# auto-leave-insert-mode (Bool):
+# Whether to leave insert mode if a non-editable element is clicked.
+# Valid values: true, false
+# Default: true
+auto-leave-insert-mode = true
+
+# auto-insert-mode (Bool):
+# Whether to automatically enter insert mode if an editable element
+# is focused after page load.
+# Valid values: true, false
+# Default: false
+auto-insert-mode = false
+
+# forward-unbound-keys (String):
+# Whether to forward unbound keys to the webview in normal mode.
+# all: Forward all unbound keys.
+# auto: Forward unbound non-alphanumeric keys.
+# none: Don't forward any keys.
+# Default: auto
+forward-unbound-keys = auto
+
+# spatial-navigation (Bool):
+# Enables or disables the Spatial Navigation feature.
+# 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 a 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.
+# Valid values: true, false
+# Default: false
+spatial-navigation = false
+
+# links-included-in-focus-chain (Bool):
+# Whether hyperlinks should be included in the keyboard focus chain.
+# Valid values: true, false
+# Default: true
+links-included-in-focus-chain = true
+
+# rocker-gestures (Bool):
+# Whether to enable Opera-like mouse rocker gestures. This disables
+# the context menu.
+# Valid values: true, false
+# Default: false
+rocker-gestures = false
+
+# mouse-zoom-divider (Int):
+# How much to divide the mouse wheel movements to translate them
+# into zoom increments.
+# Default: 512
+mouse-zoom-divider = 512
+
+
+# Configuration of the tab bar.
+[tabs]
+
+# background-tabs (Bool):
+# Whether to open new tabs (middleclick/ctrl+click) in background.
+# Valid values: true, false
+# Default: false
+background-tabs = false
+
+# select-on-remove (SelectOnRemove):
+# Which tab to select when the focused tab is removed.
+# prev: Select the tab which came before the closed one (left in
+# horizontal, above in vertical).
+# next: Select the tab which came after the closed one (right in
+# horizontal, below in vertical).
+# last-used: Select the previously selected tab.
+# Default: next
+select-on-remove = next
+
+# new-tab-position (NewTabPosition):
+# How new tabs are positioned.
+# prev: Before the current tab.
+# next: After the current tab.
+# first: At the beginning.
+# last: At the end.
+# Default: next
+new-tab-position = next
+
+# new-tab-position-explicit (NewTabPosition):
+# How new tabs opened explicitly are positioned.
+# prev: Before the current tab.
+# next: After the current tab.
+# first: At the beginning.
+# last: At the end.
+# Default: last
+new-tab-position-explicit = last
+
+# last-close (String):
+# Behavior when the last tab is closed.
+# ignore: Don't do anything.
+# blank: Load a blank page.
+# startpage: Load the start page.
+# default-page: Load the default page.
+# close: Close the window.
+# Default: ignore
+last-close = ignore
+
+# show (String):
+# When to show the tab bar
+# always: Always show the tab bar.
+# never: Always hide the tab bar.
+# multiple: Hide the tab bar if only one tab is open.
+# switching: Show the tab bar when switching tabs.
+# Default: always
+show = always
+
+# show-switching-delay (Int):
+# Time to show the tab bar before hiding it when tabs->show is set
+# to 'switching'.
+# Default: 800
+show-switching-delay = 800
+
+# wrap (Bool):
+# Whether to wrap when changing tabs.
+# Valid values: true, false
+# Default: true
+wrap = true
+
+# movable (Bool):
+# Whether tabs should be movable.
+# Valid values: true, false
+# Default: true
+movable = true
+
+# close-mouse-button (String):
+# On which mouse button to close tabs.
+# right: Close tabs on right-click.
+# middle: Close tabs on middle-click.
+# none: Don't close tabs using the mouse.
+# Default: middle
+close-mouse-button = middle
+
+# position (Position):
+# The position of the tab bar.
+# Valid values: top, bottom, left, right
+# Default: top
+position = top
+
+# show-favicons (Bool):
+# Whether to show favicons in the tab bar.
+# Valid values: true, false
+# Default: true
+show-favicons = true
+
+# favicon-scale (Float):
+# Scale for favicons in the tab bar. The tab size is unchanged, so
+# big favicons also require extra `tabs->padding`.
+# Default: 1.0
+favicon-scale = 1.0
+
+# width (PercOrInt):
+# The width of the tab bar if it's vertical, in px or as percentage
+# of the window.
+# Default: 20%
+width = 20%
+
+# pinned-width (Int):
+# The width for pinned tabs with a horizontal tabbar, in px.
+# Default: 43
+pinned-width = 43
+
+# indicator-width (Int):
+# Width of the progress indicator (0 to disable).
+# Default: 3
+indicator-width = 3
+
+# tabs-are-windows (Bool):
+# Whether to open windows instead of tabs.
+# Valid values: true, false
+# Default: false
+tabs-are-windows = false
+
+# title-format (FormatString):
+# The format to use for the tab title. The following placeholders
+# are defined:
+# * `{perc}`: The percentage as a string like `[10%]`.
+# * `{perc_raw}`: The raw percentage, e.g. `10`
+# * `{title}`: The title of the current web page
+# * `{title_sep}`: The string ` - ` if a title is set, empty
+# otherwise.
+# * `{index}`: The index of this tab.
+# * `{id}`: The internal tab ID of this tab.
+# * `{scroll_pos}`: The page scroll position.
+# * `{host}`: The host of the current web page.
+# * `{backend}`: Either 'webkit' or 'webengine'
+# * `{private}` : Indicates when private mode is enabled.
+# Default: {index}: {title}
+title-format = {index}: {title}
+
+# title-format-pinned (FormatString):
+# The format to use for the tab title for pinned tabs. The same
+# placeholders like for title-format are defined.
+# Default: {index}
+title-format-pinned = {index}
+
+# title-alignment (TextAlignment):
+# Alignment of the text inside of tabs
+# Valid values: left, right, center
+# Default: left
+title-alignment = left
+
+# mousewheel-tab-switching (Bool):
+# Switch between tabs using the mouse wheel.
+# Valid values: true, false
+# Default: true
+mousewheel-tab-switching = true
+
+# padding (Padding):
+# Padding for tabs (top, bottom, left, right).
+# Default: 0,0,5,5
+padding = 0,0,5,5
+
+# indicator-padding (Padding):
+# Padding for indicators (top, bottom, left, right).
+# Default: 2,2,0,4
+indicator-padding = 2,2,0,4
+
+
+# Settings related to cache and storage.
+[storage]
+
+# download-directory (Directory):
+# The directory to save downloads to. An empty value selects a
+# sensible os-specific default. Will expand environment variables.
+# Default:
+download-directory =
+
+# prompt-download-directory (Bool):
+# Whether to prompt the user for the download location.
+# If set to false, 'download-directory' will be used.
+# Valid values: true, false
+# Default: true
+prompt-download-directory = true
+
+# remember-download-directory (Bool):
+# Whether to remember the last used download directory.
+# Valid values: true, false
+# Default: true
+remember-download-directory = true
+
+# maximum-pages-in-cache (Int):
+# The maximum number of pages to hold in the global memory page
+# cache.
+# The Page Cache allows for a nicer user experience when navigating
+# forth or back to pages in the forward/back history, by pausing and
+# resuming up to _n_ pages.
+# For more information about the feature, please refer to:
+# http://webkit.org/blog/427/webkit-page-cache-i-the-basics/
+# Default: 0
+maximum-pages-in-cache = 0
+
+# offline-web-application-cache (Bool):
+# Whether support for the HTML 5 web application cache feature is
+# enabled.
+# 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.
+# The feature is described in details at:
+# http://dev.w3.org/html5/spec/Overview.html#appcache
+# Valid values: true, false
+# Default: true
+offline-web-application-cache = true
+
+# local-storage (Bool):
+# Whether support for HTML 5 local storage and Web SQL is enabled.
+# Valid values: true, false
+# Default: true
+local-storage = true
+
+# cache-size (Int):
+# Size of the HTTP network cache. Empty to use the default value.
+# Default:
+cache-size =
+
+
+# Loaded plugins/scripts and allowed actions.
+[content]
+
+# allow-images (Bool):
+# Whether images are automatically loaded in web pages.
+# Valid values: true, false
+# Default: true
+allow-images = true
+
+# allow-javascript (Bool):
+# Enables or disables the running of JavaScript programs.
+# Valid values: true, false
+# Default: true
+allow-javascript = true
+
+# allow-plugins (Bool):
+# Enables or disables plugins in Web pages.
+# Qt plugins with a mimetype such as "application/x-qt-plugin" are
+# not affected by this setting.
+# Valid values: true, false
+# Default: false
+allow-plugins = true
+
+# webgl (Bool):
+# Enables or disables WebGL.
+# Valid values: true, false
+# Default: true
+webgl = true
+
+# hyperlink-auditing (Bool):
+# Enable or disable hyperlink auditing (<a ping>).
+# Valid values: true, false
+# Default: false
+hyperlink-auditing = false
+
+# geolocation (BoolAsk):
+# Allow websites to request geolocations.
+# Valid values: true, false, ask
+# Default: ask
+geolocation = false
+
+# notifications (BoolAsk):
+# Allow websites to show notifications.
+# Valid values: true, false, ask
+# Default: ask
+notifications = ask
+
+# media-capture (BoolAsk):
+# Allow websites to record audio/video.
+# Valid values: true, false, ask
+# Default: ask
+media-capture = ask
+
+# javascript-can-open-windows-automatically (Bool):
+# Whether JavaScript programs can open new windows without user
+# interaction.
+# Valid values: true, false
+# Default: false
+javascript-can-open-windows-automatically = true
+
+# javascript-can-close-windows (Bool):
+# Whether JavaScript programs can close windows.
+# Valid values: true, false
+# Default: false
+javascript-can-close-windows = true
+
+# javascript-can-access-clipboard (Bool):
+# Whether JavaScript programs can read or write to the clipboard.
+# With QtWebEngine, writing the clipboard as response to a user
+# interaction is always allowed.
+# Valid values: true, false
+# Default: false
+javascript-can-access-clipboard = true
+
+# ignore-javascript-prompt (Bool):
+# Whether all javascript prompts should be ignored.
+# Valid values: true, false
+# Default: false
+ignore-javascript-prompt = true
+
+# ignore-javascript-alert (Bool):
+# Whether all javascript alerts should be ignored.
+# Valid values: true, false
+# Default: false
+ignore-javascript-alert = true
+
+# local-content-can-access-remote-urls (Bool):
+# Whether locally loaded documents are allowed to access remote
+# urls.
+# Valid values: true, false
+# Default: false
+local-content-can-access-remote-urls = false
+
+# local-content-can-access-file-urls (Bool):
+# Whether locally loaded documents are allowed to access other local
+# urls.
+# Valid values: true, false
+# Default: true
+local-content-can-access-file-urls = true
+
+# cookies-accept (String):
+# Control which cookies to accept.
+# all: Accept all cookies.
+# no-3rdparty: Accept cookies from the same origin only.
+# no-unknown-3rdparty: Accept cookies from the same origin only,
+# unless a cookie is already set for the domain.
+# never: Don't accept cookies at all.
+# Default: no-3rdparty
+cookies-accept = all
+
+# cookies-store (Bool):
+# Whether to store cookies. Note this option needs a restart with
+# QtWebEngine on Qt < 5.9.
+# Valid values: true, false
+# Default: true
+cookies-store = true
+
+# host-block-lists (List of Url):
+# List of URLs of lists which contain hosts to block.
+# The file can be in one of the following formats:
+# - An '/etc/hosts'-like file
+# - One host per line
+# - A zip-file of any of the above, with either only one file, or a
+# file named 'hosts' (with any extension).
+# Default:
+# https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext
+host-block-lists = https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext
+
+# host-blocking-enabled (Bool):
+# Whether host blocking is enabled.
+# Valid values: true, false
+# Default: true
+host-blocking-enabled = false
+
+# host-blocking-whitelist (List of String):
+# List of domains that should always be loaded, despite being
+# ad-blocked.
+# Domains may contain * and ? wildcards and are otherwise required
+# to exactly match the requested domain.
+# Local domains are always exempt from hostblocking.
+# Default: piwik.org
+host-blocking-whitelist = piwik.org,next-episode.net,churchmilitant.tv,load.sumome.com,s7.addthis.com,www.google-analytics.com,ssl.google-analytics.com,referrer.disqus.com,m.addthisedge.com,m.addthis.com,stats.g.doubleclick.net,*.addthis.com,lukesmith.xyz
+
+# enable-pdfjs (Bool):
+# Enable 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.
+# Valid values: true, false
+# Default: false
+enable-pdfjs = false
+
+
+# Hinting settings.
+[hints]
+
+# border (String):
+# CSS border value for hints.
+# Default: 1px solid #E3BE23
+border = 1px solid #E3BE23
+
+# mode (String):
+# Mode to use for hints.
+# number: Use numeric hints. (In this mode you can also type
+# letters form the hinted element to filter and reduce the number of
+# elements that are hinted.)
+# letter: Use the chars in the hints -> chars setting.
+# word: Use hints words based on the html elements and the extra
+# words.
+# Default: letter
+mode = number
+
+# chars (UniqueCharString):
+# Chars used for hint strings.
+# Default: asdfghjkl
+chars = asdfghjkl
+
+# min-chars (Int):
+# Minimum number of chars used for hint strings.
+# Default: 1
+min-chars = 1
+
+# scatter (Bool):
+# Whether to scatter hint key chains (like Vimium) or not (like
+# dwb). Ignored for number hints.
+# Valid values: true, false
+# Default: true
+scatter = true
+
+# uppercase (Bool):
+# Make chars in hint strings uppercase.
+# Valid values: true, false
+# Default: false
+uppercase = false
+
+# dictionary (File):
+# The dictionary file to be used by the word hints.
+# Default: /usr/share/dict/words
+dictionary = /usr/share/dict/words
+
+# auto-follow (String):
+# Controls when a hint can be automatically followed without the
+# user pressing Enter.
+# always: Auto-follow whenever there is only a single hint on a
+# page.
+# unique-match: Auto-follow whenever there is a unique non-empty
+# match in either the hint string (word mode) or filter (number
+# mode).
+# full-match: Follow the hint when the user typed the whole hint
+# (letter, word or number mode) or the element's text (only in
+# number mode).
+# never: The user will always need to press Enter to follow a
+# hint.
+# Default: unique-match
+auto-follow = unique-match
+
+# auto-follow-timeout (Int):
+# A timeout (in milliseconds) to inhibit normal-mode key bindings
+# after a successful auto-follow.
+# Default: 0
+auto-follow-timeout = 0
+
+# next-regexes (List of Regex):
+# A comma-separated list of regexes to use for 'next' links.
+# Default:
+# \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b
+next-regexes = \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b
+
+# prev-regexes (List of Regex):
+# A comma-separated list of regexes to use for 'prev' links.
+# Default: \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b
+prev-regexes = \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b
+
+# find-implementation (String):
+# Which implementation to use to find elements to hint.
+# javascript: Better but slower
+# python: Slightly worse but faster
+# Default: python
+find-implementation = python
+
+# hide-unmatched-rapid-hints (Bool):
+# Controls hiding unmatched hints in rapid mode.
+# Valid values: true, false
+# Default: true
+hide-unmatched-rapid-hints = true
+
+
+# Definitions of search engines which can be used via the address bar.
+# The searchengine named `DEFAULT` is used when `general -> auto-search`
+# is true and something else than a URL was entered to be opened. Other
+# search engines can be used by prepending the search engine name to the
+# search term, e.g. `:open google qutebrowser`. The string `{}` will be
+# replaced by the search term, use `{{` and `}}` for literal `{`/`}`
+# signs.
+[searchengines]
+DEFAULT = https://duckduckgo.com/?q={}
+
+
+# Aliases for commands.
+# By default, no aliases are defined. Example which adds a new command
+# `:qtb` to open qutebrowsers website:
+# `qtb = open https://www.qutebrowser.org/`
+[aliases]
+qtb = open https://www.qutebrowser.org/
+mbt = open t https://signin1.bt.com/login/emailloginform
+Ombt = open -t https://signin1.bt.com/login/emailloginform
+nx = open http://next-episode.net/
+Onx = open -t http://next-episode.net/
+gk = open https://gameknot.com/
+Ogk = open -t https://gameknot.com/
+yt = open https://youtube.com
+Oyt = open -t https://youtube.com
+Y = open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files
+OY = open -t http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files
+1337 = open https://1337x.to/home
+O1337 = open -t https://1337x.to/home
+ez = open https://eztv.ag
+Oez = open -t https://eztv.ag
+tz = open https://extratorrent.ag
+Otz = open -t https://extratorrent.ag
+tpb = open https://thepiratebay.org/
+Otpb = open -t https://thepiratebay.org/
+Nm = open https://netmail.herts.ac.uk/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fnetmail.herts.ac.uk%2fowa
+ONm = open -t https://netmail.herts.ac.uk/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fnetmail.herts.ac.uk%2fowa
+
+
+# Colors used in the UI.
+# A value can be in one of the following format:
+# * `#RGB`/`#RRGGBB`/`#RRRGGGBBB`/`#RRRRGGGGBBBB`
+# * An SVG color name as specified in http://www.w3.org/TR/SVG/types.html#ColorKeywords[the W3C specification].
+# * transparent (no color)
+# * `rgb(r, g, b)` / `rgba(r, g, b, a)` (values 0-255 or percentages)
+# * `hsv(h, s, v)` / `hsva(h, s, v, a)` (values 0-255, hue 0-359)
+# * A gradient as explained in http://doc.qt.io/qt-5/stylesheet-reference.html#list-of-property-types[the Qt documentation] under ``Gradient''.
+# A *.system value determines the color system to use for color
+# interpolation between similarly-named *.start and *.stop entries,
+# regardless of how they are defined in the options. Valid values are
+# 'rgb', 'hsv', and 'hsl'.
+# The `hints.*` values are a special case as they're real CSS colors, not Qt-CSS colors. There, for a gradient, you need to use `-webkit-gradient`, see https://www.webkit.org/blog/175/introducing-css-gradients/[the WebKit documentation].
+[colors]
+
+# completion.fg (QtColor):
+# Text color of the completion widget.
+# Default: white
+completion.fg = white
+
+# completion.bg (QssColor):
+# Background color of the completion widget.
+# Default: #333333
+completion.bg = #333333
+
+# completion.alternate-bg (QssColor):
+# Alternating background color of the completion widget.
+# Default: #444444
+completion.alternate-bg = #444444
+
+# completion.category.fg (QtColor):
+# Foreground color of completion widget category headers.
+# Default: white
+completion.category.fg = white
+
+# completion.category.bg (QssColor):
+# Background color of the completion widget category headers.
+# Default: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888,
+# stop:1 #505050)
+completion.category.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050)
+
+# completion.category.border.top (QssColor):
+# Top border color of the completion widget category headers.
+# Default: black
+completion.category.border.top = black
+
+# completion.category.border.bottom (QssColor):
+# Bottom border color of the completion widget category headers.
+# Default: ${completion.category.border.top}
+completion.category.border.bottom = ${completion.category.border.top}
+
+# completion.item.selected.fg (QtColor):
+# Foreground color of the selected completion item.
+# Default: black
+completion.item.selected.fg = black
+
+# completion.item.selected.bg (QssColor):
+# Background color of the selected completion item.
+# Default: #e8c000
+completion.item.selected.bg = #e8c000
+
+# completion.item.selected.border.top (QssColor):
+# Top border color of the completion widget category headers.
+# Default: #bbbb00
+completion.item.selected.border.top = #bbbb00
+
+# completion.item.selected.border.bottom (QssColor):
+# Bottom border color of the selected completion item.
+# Default: ${completion.item.selected.border.top}
+completion.item.selected.border.bottom = ${completion.item.selected.border.top}
+
+# completion.match.fg (QssColor):
+# Foreground color of the matched text in the completion.
+# Default: #ff4444
+completion.match.fg = #ff4444
+
+# completion.scrollbar.fg (QssColor):
+# Color of the scrollbar handle in completion view.
+# Default: ${completion.fg}
+completion.scrollbar.fg = ${completion.fg}
+
+# completion.scrollbar.bg (QssColor):
+# Color of the scrollbar in completion view
+# Default: ${completion.bg}
+completion.scrollbar.bg = ${completion.bg}
+
+# statusbar.fg (QssColor):
+# Foreground color of the statusbar.
+# Default: white
+statusbar.fg = white
+
+# statusbar.bg (QssColor):
+# Background color of the statusbar.
+# Default: black
+statusbar.bg = black
+
+# statusbar.fg.private (QssColor):
+# Foreground color of the statusbar in private browsing mode.
+# Default: ${statusbar.fg}
+statusbar.fg.private = ${statusbar.fg}
+
+# statusbar.bg.private (QssColor):
+# Background color of the statusbar in private browsing mode.
+# Default: #666666
+statusbar.bg.private = #666666
+
+# statusbar.fg.insert (QssColor):
+# Foreground color of the statusbar in insert mode.
+# Default: ${statusbar.fg}
+statusbar.fg.insert = ${statusbar.fg}
+
+# statusbar.bg.insert (QssColor):
+# Background color of the statusbar in insert mode.
+# Default: darkgreen
+statusbar.bg.insert = darkgreen
+
+# statusbar.fg.command (QssColor):
+# Foreground color of the statusbar in command mode.
+# Default: ${statusbar.fg}
+statusbar.fg.command = ${statusbar.fg}
+
+# statusbar.bg.command (QssColor):
+# Background color of the statusbar in command mode.
+# Default: ${statusbar.bg}
+statusbar.bg.command = ${statusbar.bg}
+
+# statusbar.fg.command.private (QssColor):
+# Foreground color of the statusbar in private browsing + command
+# mode.
+# Default: ${statusbar.fg.private}
+statusbar.fg.command.private = ${statusbar.fg.private}
+
+# statusbar.bg.command.private (QssColor):
+# Background color of the statusbar in private browsing + command
+# mode.
+# Default: ${statusbar.bg.private}
+statusbar.bg.command.private = ${statusbar.bg.private}
+
+# statusbar.fg.caret (QssColor):
+# Foreground color of the statusbar in caret mode.
+# Default: ${statusbar.fg}
+statusbar.fg.caret = ${statusbar.fg}
+
+# statusbar.bg.caret (QssColor):
+# Background color of the statusbar in caret mode.
+# Default: purple
+statusbar.bg.caret = purple
+
+# statusbar.fg.caret-selection (QssColor):
+# Foreground color of the statusbar in caret mode with a selection
+# Default: ${statusbar.fg}
+statusbar.fg.caret-selection = ${statusbar.fg}
+
+# statusbar.bg.caret-selection (QssColor):
+# Background color of the statusbar in caret mode with a selection
+# Default: #a12dff
+statusbar.bg.caret-selection = #a12dff
+
+# statusbar.progress.bg (QssColor):
+# Background color of the progress bar.
+# Default: white
+statusbar.progress.bg = white
+
+# statusbar.url.fg (QssColor):
+# Default foreground color of the URL in the statusbar.
+# Default: ${statusbar.fg}
+statusbar.url.fg = ${statusbar.fg}
+
+# statusbar.url.fg.success (QssColor):
+# Foreground color of the URL in the statusbar on successful load
+# (http).
+# Default: white
+statusbar.url.fg.success = white
+
+# statusbar.url.fg.success.https (QssColor):
+# Foreground color of the URL in the statusbar on successful load
+# (https).
+# Default: lime
+statusbar.url.fg.success.https = lime
+
+# statusbar.url.fg.error (QssColor):
+# Foreground color of the URL in the statusbar on error.
+# Default: orange
+statusbar.url.fg.error = orange
+
+# statusbar.url.fg.warn (QssColor):
+# Foreground color of the URL in the statusbar when there's a
+# warning.
+# Default: yellow
+statusbar.url.fg.warn = yellow
+
+# statusbar.url.fg.hover (QssColor):
+# Foreground color of the URL in the statusbar for hovered links.
+# Default: aqua
+statusbar.url.fg.hover = aqua
+
+# tabs.fg.odd (QtColor):
+# Foreground color of unselected odd tabs.
+# Default: white
+tabs.fg.odd = white
+
+# tabs.bg.odd (QtColor):
+# Background color of unselected odd tabs.
+# Default: grey
+tabs.bg.odd = grey
+
+# tabs.fg.even (QtColor):
+# Foreground color of unselected even tabs.
+# Default: white
+tabs.fg.even = white
+
+# tabs.bg.even (QtColor):
+# Background color of unselected even tabs.
+# Default: darkgrey
+tabs.bg.even = darkgrey
+
+# tabs.fg.selected.odd (QtColor):
+# Foreground color of selected odd tabs.
+# Default: white
+tabs.fg.selected.odd = white
+
+# tabs.bg.selected.odd (QtColor):
+# Background color of selected odd tabs.
+# Default: black
+tabs.bg.selected.odd = black
+
+# tabs.fg.selected.even (QtColor):
+# Foreground color of selected even tabs.
+# Default: ${tabs.fg.selected.odd}
+tabs.fg.selected.even = ${tabs.fg.selected.odd}
+
+# tabs.bg.selected.even (QtColor):
+# Background color of selected even tabs.
+# Default: ${tabs.bg.selected.odd}
+tabs.bg.selected.even = ${tabs.bg.selected.odd}
+
+# tabs.bg.bar (QtColor):
+# Background color of the tab bar.
+# Default: #555555
+tabs.bg.bar = #555555
+
+# tabs.indicator.start (QtColor):
+# Color gradient start for the tab indicator.
+# Default: #0000aa
+tabs.indicator.start = #0000aa
+
+# tabs.indicator.stop (QtColor):
+# Color gradient end for the tab indicator.
+# Default: #00aa00
+tabs.indicator.stop = #00aa00
+
+# tabs.indicator.error (QtColor):
+# Color for the tab indicator on errors..
+# Default: #ff0000
+tabs.indicator.error = #ff0000
+
+# tabs.indicator.system (ColorSystem):
+# Color gradient interpolation system for the tab indicator.
+# rgb: Interpolate in the RGB color system.
+# hsv: Interpolate in the HSV color system.
+# hsl: Interpolate in the HSL color system.
+# none: Don't show a gradient.
+# Default: rgb
+tabs.indicator.system = rgb
+
+# hints.fg (QssColor):
+# Font color for hints.
+# Default: black
+hints.fg = black
+
+# hints.bg (QssColor):
+# Background color for hints. Note that you can use a `rgba(...)`
+# value for transparency.
+# Default: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255,
+# 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8))
+hints.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8))
+
+# hints.fg.match (QssColor):
+# Font color for the matched part of hints.
+# Default: green
+hints.fg.match = green
+
+# downloads.bg.bar (QssColor):
+# Background color for the download bar.
+# Default: black
+downloads.bg.bar = black
+
+# downloads.fg.start (QtColor):
+# Color gradient start for download text.
+# Default: white
+downloads.fg.start = white
+
+# downloads.bg.start (QtColor):
+# Color gradient start for download backgrounds.
+# Default: #0000aa
+downloads.bg.start = #0000aa
+
+# downloads.fg.stop (QtColor):
+# Color gradient end for download text.
+# Default: ${downloads.fg.start}
+downloads.fg.stop = ${downloads.fg.start}
+
+# downloads.bg.stop (QtColor):
+# Color gradient stop for download backgrounds.
+# Default: #00aa00
+downloads.bg.stop = #00aa00
+
+# downloads.fg.system (ColorSystem):
+# Color gradient interpolation system for download text.
+# rgb: Interpolate in the RGB color system.
+# hsv: Interpolate in the HSV color system.
+# hsl: Interpolate in the HSL color system.
+# none: Don't show a gradient.
+# Default: rgb
+downloads.fg.system = rgb
+
+# downloads.bg.system (ColorSystem):
+# Color gradient interpolation system for download backgrounds.
+# rgb: Interpolate in the RGB color system.
+# hsv: Interpolate in the HSV color system.
+# hsl: Interpolate in the HSL color system.
+# none: Don't show a gradient.
+# Default: rgb
+downloads.bg.system = rgb
+
+# downloads.fg.error (QtColor):
+# Foreground color for downloads with errors.
+# Default: white
+downloads.fg.error = white
+
+# downloads.bg.error (QtColor):
+# Background color for downloads with errors.
+# Default: red
+downloads.bg.error = red
+
+# webpage.bg (QtColor):
+# Background color for webpages if unset (or empty to use the
+# theme's color)
+# Default: white
+webpage.bg = white
+
+# keyhint.fg (QssColor):
+# Text color for the keyhint widget.
+# Default: #FFFFFF
+keyhint.fg = #FFFFFF
+
+# keyhint.fg.suffix (CssColor):
+# Highlight color for keys to complete the current keychain
+# Default: #FFFF00
+keyhint.fg.suffix = #FFFF00
+
+# keyhint.bg (QssColor):
+# Background color of the keyhint widget.
+# Default: rgba(0, 0, 0, 80%)
+keyhint.bg = rgba(0, 0, 0, 80%)
+
+# messages.fg.error (QssColor):
+# Foreground color of an error message.
+# Default: white
+messages.fg.error = white
+
+# messages.bg.error (QssColor):
+# Background color of an error message.
+# Default: red
+messages.bg.error = red
+
+# messages.border.error (QssColor):
+# Border color of an error message.
+# Default: #bb0000
+messages.border.error = #bb0000
+
+# messages.fg.warning (QssColor):
+# Foreground color a warning message.
+# Default: white
+messages.fg.warning = white
+
+# messages.bg.warning (QssColor):
+# Background color of a warning message.
+# Default: darkorange
+messages.bg.warning = darkorange
+
+# messages.border.warning (QssColor):
+# Border color of an error message.
+# Default: #d47300
+messages.border.warning = #d47300
+
+# messages.fg.info (QssColor):
+# Foreground color an info message.
+# Default: white
+messages.fg.info = white
+
+# messages.bg.info (QssColor):
+# Background color of an info message.
+# Default: black
+messages.bg.info = black
+
+# messages.border.info (QssColor):
+# Border color of an info message.
+# Default: #333333
+messages.border.info = #333333
+
+# prompts.fg (QssColor):
+# Foreground color for prompts.
+# Default: white
+prompts.fg = white
+
+# prompts.bg (QssColor):
+# Background color for prompts.
+# Default: darkblue
+prompts.bg = darkblue
+
+# prompts.selected.bg (QssColor):
+# Background color for the selected item in filename prompts.
+# Default: #308cc6
+prompts.selected.bg = #308cc6
+
+
+# Fonts used for the UI, with optional style/weight/size.
+# * Style: `normal`/`italic`/`oblique`
+# * Weight: `normal`, `bold`, `100`..`900`
+# * Size: _number_ `px`/`pt`
+[fonts]
+
+# _monospace (Font):
+# Default monospace fonts.
+# Default: xos4 Terminus, Terminus, Monospace, "DejaVu Sans Mono",
+# Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New",
+# Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal
+_monospace = Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal
+
+# completion (Font):
+# Font used in the completion widget.
+# Default: 8pt ${_monospace}
+completion = 8pt ${_monospace}
+
+# completion.category (Font):
+# Font used in the completion categories.
+# Default: bold ${completion}
+completion.category = bold ${completion}
+
+# tabbar (QtFont):
+# Font used in the tab bar.
+# Default: 8pt ${_monospace}
+tabbar = 8pt ${_monospace}
+
+# statusbar (Font):
+# Font used in the statusbar.
+# Default: 8pt ${_monospace}
+statusbar = 8pt ${_monospace}
+
+# downloads (Font):
+# Font used for the downloadbar.
+# Default: 8pt ${_monospace}
+downloads = 8pt ${_monospace}
+
+# hints (Font):
+# Font used for the hints.
+# Default: bold 13px ${_monospace}
+hints = bold 13px ${_monospace}
+
+# debug-console (QtFont):
+# Font used for the debugging console.
+# Default: 8pt ${_monospace}
+debug-console = 8pt ${_monospace}
+
+# web-family-standard (FontFamily):
+# Font family for standard fonts.
+# Default:
+web-family-standard =
+
+# web-family-fixed (FontFamily):
+# Font family for fixed fonts.
+# Default:
+web-family-fixed =
+
+# web-family-serif (FontFamily):
+# Font family for serif fonts.
+# Default:
+web-family-serif =
+
+# web-family-sans-serif (FontFamily):
+# Font family for sans-serif fonts.
+# Default:
+web-family-sans-serif =
+
+# web-family-cursive (FontFamily):
+# Font family for cursive fonts.
+# Default:
+web-family-cursive =
+
+# web-family-fantasy (FontFamily):
+# Font family for fantasy fonts.
+# Default:
+web-family-fantasy =
+
+# web-size-minimum (Int):
+# The hard minimum font size.
+# Default: 0
+web-size-minimum = 0
+
+# web-size-minimum-logical (Int):
+# The minimum logical font size that is applied when zooming out.
+# Default: 6
+web-size-minimum-logical = 6
+
+# web-size-default (Int):
+# The default font size for regular text.
+# Default: 16
+web-size-default = 16
+
+# web-size-default-fixed (Int):
+# The default font size for fixed-pitch text.
+# Default: 13
+web-size-default-fixed = 13
+
+# keyhint (Font):
+# Font used in the keyhint widget.
+# Default: 8pt ${_monospace}
+keyhint = 8pt ${_monospace}
+
+# messages.error (Font):
+# Font used for error messages.
+# Default: 8pt ${_monospace}
+messages.error = 8pt ${_monospace}
+
+# messages.warning (Font):
+# Font used for warning messages.
+# Default: 8pt ${_monospace}
+messages.warning = 8pt ${_monospace}
+
+# messages.info (Font):
+# Font used for info messages.
+# Default: 8pt ${_monospace}
+messages.info = 8pt ${_monospace}
+
+# prompts (Font):
+# Font used for prompts.
+# Default: 8pt sans-serif
+prompts = 8pt sans-serif
diff --git a/.config/qutebrowser/keys.conf b/.config/qutebrowser/keys.conf
new file mode 100755
index 0000000..06fc600
--- /dev/null
+++ b/.config/qutebrowser/keys.conf
@@ -0,0 +1,709 @@
+# vim: ft=conf
+#
+# In this config file, qutebrowser's key bindings are configured.
+# The format looks like this:
+#
+# [keymode]
+#
+# command
+# keychain
+# keychain2
+# ...
+#
+# All blank lines and lines starting with '#' are ignored.
+# Inline-comments are not permitted.
+#
+# keymode is a comma separated list of modes in which the key binding should be
+# active. If keymode starts with !, the key binding is active in all modes
+# except the listed modes.
+#
+# For special keys (can't be part of a keychain), enclose them in `<`...`>`.
+# For modifiers, you can use either `-` or `+` as delimiters, and these names:
+#
+# * Control: `Control`, `Ctrl`
+# * Meta: `Meta`, `Windows`, `Mod4`
+# * Alt: `Alt`, `Mod1`
+# * Shift: `Shift`
+#
+# For simple keys (no `<>`-signs), a capital letter means the key is pressed
+# with Shift. For special keys (with `<>`-signs), you need to explicitly add
+# `Shift-` to match a key pressed with shift.
+#
+# Note that default keybindings are always bound, and need to be explicitly
+# unbound if you wish to remove them:
+#
+# <unbound>
+# keychain
+# keychain2
+# ...
+
+[!normal]
+
+leave-mode
+ <escape>
+ <ctrl-[>
+
+[normal]
+# Keybindings for normal mode.
+
+clear-keychain ;; search ;; fullscreen --leave
+ <escape>
+ <ctrl-[>
+
+set-cmd-text -s :open
+ o
+
+set-cmd-text :open {url:pretty}
+ go
+
+set-cmd-text -s :open -t
+ O
+
+set-cmd-text :open -t -i {url:pretty}
+ gO
+
+set-cmd-text -s :open -b
+ xo
+
+set-cmd-text :open -b -i {url:pretty}
+ xO
+
+set-cmd-text -s :open -w
+ wo
+
+set-cmd-text :open -w {url:pretty}
+ wO
+
+set-cmd-text /
+ /
+
+set-cmd-text ?
+ ?
+
+set-cmd-text :
+ :
+
+open -t
+ ga
+ <ctrl-t>
+
+open -w
+ <ctrl-n>
+
+tab-close
+ d
+ <ctrl-w>
+
+tab-close -o
+ D
+
+tab-only
+ co
+
+tab-focus
+ T
+
+tab-move
+ gm
+
+tab-move -
+ gl
+
+tab-move +
+ gr
+
+tab-next
+ J
+ <ctrl-pgdown>
+
+tab-prev
+ K
+ <ctrl-pgup>
+
+tab-clone
+ gC
+
+reload
+ r
+ <f5>
+
+reload -f
+ R
+ <ctrl-f5>
+
+back
+ H
+ <back>
+
+back -t
+ th
+
+back -w
+ wh
+
+forward
+ L
+ <forward>
+
+forward -t
+ tl
+
+forward -w
+ wl
+
+fullscreen
+ <f11>
+
+hint
+ f
+
+hint all tab
+ F
+
+hint all window
+ wf
+
+hint all tab-bg
+ ;b
+
+hint all tab-fg
+ ;f
+
+hint all hover
+ ;h
+
+hint images
+ ;i
+
+hint images tab
+ ;I
+
+hint links fill :open {hint-url}
+ ;o
+
+hint links fill :open -t -i {hint-url}
+ ;O
+
+hint links yank
+ ;y
+
+hint links yank-primary
+ ;Y
+
+hint --rapid links tab-bg
+ ;r
+
+hint --rapid links window
+ ;R
+
+hint links download
+ ;d
+
+hint inputs
+ ;t
+
+scroll left
+ h
+
+scroll down
+ j
+
+scroll up
+ k
+
+scroll right
+ l
+
+undo
+ u
+ <ctrl-shift-t>
+
+scroll-perc 0
+ gg
+
+scroll-perc
+ G
+
+search-next
+ n
+
+search-prev
+ N
+
+enter-mode insert
+ i
+
+enter-mode caret
+ v
+
+enter-mode set_mark
+ `
+
+enter-mode jump_mark
+ '
+
+yank
+ yy
+
+yank -s
+ yY
+
+yank title
+ yt
+
+yank title -s
+ yT
+
+yank domain
+ yd
+
+yank domain -s
+ yD
+
+yank pretty-url
+ yp
+
+yank pretty-url -s
+ yP
+
+open -- {clipboard}
+ pp
+
+open -- {primary}
+ pP
+
+open -t -- {clipboard}
+ Pp
+
+open -t -- {primary}
+ PP
+
+open -w -- {clipboard}
+ wp
+
+open -w -- {primary}
+ wP
+
+set-cmd-text -s :quickmark-load
+ b
+
+set-cmd-text -s :quickmark-load -t
+ B
+
+set-cmd-text -s :quickmark-load -w
+ wb
+
+set-cmd-text -s :bookmark-load
+ gb
+
+set-cmd-text -s :bookmark-load -t
+ gB
+
+set-cmd-text -s :bookmark-load -w
+ wB
+
+save
+ sf
+
+set-cmd-text -s :set
+ ss
+
+set-cmd-text -s :set -t
+ sl
+
+set-cmd-text -s :bind
+ sk
+
+zoom-out
+ -
+
+zoom-in
+ +
+
+zoom
+ =
+
+navigate prev
+ [[
+
+navigate next
+ ]]
+
+navigate prev -t
+ {{
+
+navigate next -t
+ }}
+
+navigate up
+ gu
+
+navigate up -t
+ gU
+
+navigate increment
+ <ctrl-a>
+
+navigate decrement
+ <ctrl-x>
+
+inspector
+ wi
+
+download
+ gd
+
+download-cancel
+ ad
+
+download-clear
+ cd
+
+view-source
+ gf
+
+set-cmd-text -s :buffer
+ gt
+
+tab-focus last
+ <ctrl-tab>
+
+enter-mode passthrough
+ <ctrl-v>
+
+quit
+ <ctrl-q>
+
+scroll-page 0 1
+ <ctrl-f>
+
+scroll-page 0 -1
+ <ctrl-b>
+
+scroll-page 0 0.5
+ <ctrl-d>
+
+scroll-page 0 -0.5
+ <ctrl-u>
+
+tab-focus 1
+ <alt-1>
+
+tab-focus 2
+ <alt-2>
+
+tab-focus 3
+ <alt-3>
+
+tab-focus 4
+ <alt-4>
+
+tab-focus 5
+ <alt-5>
+
+tab-focus 6
+ <alt-6>
+
+tab-focus 7
+ <alt-7>
+
+tab-focus 8
+ <alt-8>
+
+tab-focus 9
+ <alt-9>
+
+home
+ <ctrl-h>
+
+stop
+ <ctrl-s>
+
+print
+ <ctrl-alt-p>
+
+open qute:settings
+ Ss
+
+follow-selected
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+follow-selected -t
+ <ctrl-return>
+ <ctrl-enter>
+
+repeat-command
+ .
+
+record-macro
+ q
+
+run-macro
+ @
+
+spawn mpv {url}
+ mpv
+ m
+
+hint links spawn umpv {hint-urls}
+ MPV
+
+hint links spawn mpv {hint-url}
+ M
+
+spawn tsp-ytdl {hint-url}
+ ;M
+
+wq
+ ZZ
+
+tab-focus -1
+ g$
+
+tab-pin
+ <ctrl-p>
+
+[insert]
+# Keybindings for insert mode.
+# Since normal keypresses are passed through, only special keys are
+# supported in this mode.
+# Useful hidden commands to map in this section:
+# * `open-editor`: Open a texteditor with the focused field.
+# * `paste-primary`: Paste primary selection at cursor position.
+
+open-editor
+ <ctrl-e>
+
+insert-text {primary}
+ <shift-ins>
+
+[hint]
+# Keybindings for hint mode.
+# Since normal keypresses are passed through, only special keys are
+# supported in this mode.
+# Useful hidden commands to map in this section:
+# * `follow-hint`: Follow the currently selected hint.
+
+follow-hint
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+hint --rapid links tab-bg
+ <ctrl-r>
+
+hint links
+ <ctrl-f>
+
+hint all tab-bg
+ <ctrl-b>
+
+[command]
+# Keybindings for command mode.
+# Since normal keypresses are passed through, only special keys are
+# supported in this mode.
+# Useful hidden commands to map in this section:
+# * `command-history-prev`: Switch to previous command in history.
+# * `command-history-next`: Switch to next command in history.
+# * `completion-item-focus`: Select another item in completion.
+# * `command-accept`: Execute the command currently in the commandline.
+
+command-history-prev
+ <ctrl-p>
+
+command-history-next
+ <ctrl-n>
+
+completion-item-focus prev
+ <shift-tab>
+ <up>
+
+completion-item-focus next
+ <tab>
+ <down>
+
+completion-item-focus next-category
+ <ctrl-tab>
+
+completion-item-focus prev-category
+ <ctrl-shift-tab>
+
+completion-item-del
+ <ctrl-d>
+
+command-accept
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+[prompt]
+# Keybindings for prompts in the status line.
+# You can bind normal keys in this mode, but they will be only active
+# when a yes/no-prompt is asked. For other prompt modes, you can only
+# bind special keys.
+# Useful hidden commands to map in this section:
+# * `prompt-accept`: Confirm the entered value.
+# * `prompt-accept yes`: Answer yes to a yes/no question.
+# * `prompt-accept no`: Answer no to a yes/no question.
+
+prompt-accept
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+prompt-accept yes
+ y
+
+prompt-accept no
+ n
+
+prompt-open-download
+ <ctrl-x>
+
+prompt-item-focus prev
+ <shift-tab>
+ <up>
+
+prompt-item-focus next
+ <tab>
+ <down>
+
+[command,prompt]
+
+rl-backward-char
+ <ctrl-b>
+
+rl-forward-char
+ <ctrl-f>
+
+rl-backward-word
+ <alt-b>
+
+rl-forward-word
+ <alt-f>
+
+rl-beginning-of-line
+ <ctrl-a>
+
+rl-end-of-line
+ <ctrl-e>
+
+rl-unix-line-discard
+ <ctrl-u>
+
+rl-kill-line
+ <ctrl-k>
+
+rl-kill-word
+ <alt-d>
+
+rl-unix-word-rubout
+ <ctrl-w>
+
+rl-backward-kill-word
+ <alt-backspace>
+
+rl-yank
+ <ctrl-y>
+
+rl-delete-char
+ <ctrl-?>
+
+rl-backward-delete-char
+ <ctrl-h>
+
+[caret]
+
+toggle-selection
+ v
+ <space>
+
+drop-selection
+ <ctrl-space>
+
+enter-mode normal
+ c
+
+move-to-next-line
+ j
+
+move-to-prev-line
+ k
+
+move-to-next-char
+ l
+
+move-to-prev-char
+ h
+
+move-to-end-of-word
+ e
+
+move-to-next-word
+ w
+
+move-to-prev-word
+ b
+
+move-to-start-of-next-block
+ ]
+
+move-to-start-of-prev-block
+ [
+
+move-to-end-of-next-block
+ }
+
+move-to-end-of-prev-block
+ {
+
+move-to-start-of-line
+ 0
+
+move-to-end-of-line
+ $
+
+move-to-start-of-document
+ gg
+
+move-to-end-of-document
+ G
+
+yank selection -s
+ Y
+
+yank selection
+ y
+ <return>
+ <ctrl-m>
+ <ctrl-j>
+ <shift-return>
+ <enter>
+ <shift-enter>
+
+scroll left
+ H
+
+scroll down
+ J
+
+scroll up
+ K
+
+scroll right
+ L
+
diff --git a/.config/qutebrowser/misc/Makefile b/.config/qutebrowser/misc/Makefile
new file mode 100644
index 0000000..210480e
--- /dev/null
+++ b/.config/qutebrowser/misc/Makefile
@@ -0,0 +1,33 @@
+PYTHON = python3
+PREFIX = /usr/local
+DESTDIR =
+ICONSIZES = 16 24 32 48 64 128 256 512
+
+SETUPTOOLSOPTIONS =
+ifdef DESTDIR
+SETUPTOOLSOPTS = --root="$(DESTDIR)"
+endif
+
+.PHONY: install
+
+doc/qutebrowser.1.html:
+ a2x -f manpage doc/qutebrowser.1.asciidoc
+
+install: doc/qutebrowser.1.html
+ $(PYTHON) setup.py install --prefix="$(PREFIX)" --optimize=1 $(SETUPTOOLSOPTS)
+ install -Dm644 misc/qutebrowser.appdata.xml \
+ "$(DESTDIR)$(PREFIX)/share/metainfo/qutebrowser.appdata.xml"
+ install -Dm644 doc/qutebrowser.1 \
+ "$(DESTDIR)$(PREFIX)/share/man/man1/qutebrowser.1"
+ install -Dm644 misc/qutebrowser.desktop \
+ "$(DESTDIR)$(PREFIX)/share/applications/qutebrowser.desktop"
+ $(foreach i,$(ICONSIZES),install -Dm644 "icons/qutebrowser-$(i)x$(i).png" \
+ "$(DESTDIR)$(PREFIX)/share/icons/hicolor/$(i)x$(i)/apps/qutebrowser.png";)
+ install -Dm644 icons/qutebrowser.svg \
+ "$(DESTDIR)$(PREFIX)/share/icons/hicolor/scalable/apps/qutebrowser.svg"
+ install -Dm755 -t "$(DESTDIR)$(PREFIX)/share/qutebrowser/userscripts/" \
+ $(wildcard misc/userscripts/*)
+ install -Dm755 -t "$(DESTDIR)$(PREFIX)/share/qutebrowser/scripts/" \
+ $(filter-out scripts/__init__.py scripts/__pycache__ scripts/dev \
+ scripts/testbrowser scripts/asciidoc2html.py scripts/setupcommon.py \
+ scripts/link_pyqt.py,$(wildcard scripts/*))
diff --git a/.config/qutebrowser/misc/apparmor/usr.bin.qutebrowser b/.config/qutebrowser/misc/apparmor/usr.bin.qutebrowser
new file mode 100644
index 0000000..b993e00
--- /dev/null
+++ b/.config/qutebrowser/misc/apparmor/usr.bin.qutebrowser
@@ -0,0 +1,41 @@
+# AppArmor profile for qutebrowser
+# Tested on Debian jessie
+
+#include <tunables/global>
+
+profile qutebrowser /usr/{local/,}bin/qutebrowser {
+
+ #include <abstractions/base>
+ #include <abstractions/nameservice>
+ #include <abstractions/openssl>
+ #include <abstractions/ssl_certs>
+ #include <abstractions/audio>
+ #include <abstractions/fonts>
+ #include <abstractions/kde>
+ #include <abstractions/user-download>
+ #include <abstractions/X>
+
+ capability dac_override,
+
+ /usr/{local/,}bin/ r,
+ /usr/{local/,}bin/qutebrowser rix,
+ /usr/bin/python3.? r,
+
+ /usr/lib/python3/ mr,
+ /usr/lib/python3/** mr,
+ /usr/lib/python3.?/ r,
+ /usr/lib/python3.?/** mr,
+ /usr/local/lib/python3.?/** r,
+
+ /proc/*/mounts r,
+ owner /tmp/** rwkl,
+ owner /run/user/*/ rw,
+ owner /run/user/*/** krw,
+
+ @{HOME}/.config/qutebrowser/** krw,
+ @{HOME}/.local/share/qutebrowser/** krw,
+ @{HOME}/.cache/qutebrowser/** krw,
+ @{HOME}/.gstreamer-0.10/* r,
+
+}
+
diff --git a/.config/qutebrowser/misc/cheatsheet.svg b/.config/qutebrowser/misc/cheatsheet.svg
new file mode 100644
index 0000000..bb87142
--- /dev/null
+++ b/.config/qutebrowser/misc/cheatsheet.svg
@@ -0,0 +1,3726 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Created with Inkscape (http://www.inkscape.org/) -->
+
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ width="1092.2667"
+ height="682.66669"
+ id="svg2"
+ sodipodi:version="0.32"
+ inkscape:version="0.92.2 5c3e80d, 2017-08-06"
+ version="1.0"
+ sodipodi:docname="cheatsheet.svg"
+ inkscape:output_extension="org.inkscape.output.svg.inkscape"
+ inkscape:export-filename="/home/vav/images/xmonad/xmbindings_lg.png"
+ inkscape:export-xdpi="112.5"
+ inkscape:export-ydpi="112.5">
+ <defs
+ id="defs4" />
+ <sodipodi:namedview
+ id="base"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ gridtolerance="10000"
+ guidetolerance="10"
+ objecttolerance="10"
+ inkscape:pageopacity="0.0"
+ inkscape:pageshadow="2"
+ inkscape:zoom="1.7536248"
+ inkscape:cx="430.72917"
+ inkscape:cy="268.64059"
+ inkscape:document-units="px"
+ inkscape:current-layer="layer1"
+ width="1024px"
+ height="640px"
+ showgrid="false"
+ inkscape:window-width="2560"
+ inkscape:window-height="1440"
+ inkscape:window-x="0"
+ inkscape:window-y="0"
+ showguides="true"
+ inkscape:guide-bbox="true"
+ inkscape:window-maximized="0"
+ inkscape:snap-text-baseline="true"
+ inkscape:measure-start="0,0"
+ inkscape:measure-end="0,0">
+ <inkscape:grid
+ id="GridFromPre046Settings"
+ type="xygrid"
+ originx="0"
+ originy="0"
+ spacingx="2.1333333"
+ spacingy="2.1333333"
+ color="#3f3fff"
+ empcolor="#3f3fff"
+ opacity="0.15"
+ empopacity="0.38"
+ empspacing="5" />
+ </sodipodi:namedview>
+ <metadata
+ id="metadata7">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <g
+ inkscape:label="Layer 1"
+ inkscape:groupmode="layer"
+ id="layer1">
+ <rect
+ ry="4.7797003"
+ y="163.79405"
+ x="10.666667"
+ height="64"
+ width="74.666664"
+ id="rect3328"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3330"
+ width="64"
+ height="64"
+ x="96"
+ y="163.79405"
+ ry="4.7797003" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3382"
+ width="63.461262"
+ height="64"
+ x="544.48407"
+ y="163.79405"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="163.79405"
+ x="619.1535"
+ height="64"
+ width="63.461262"
+ id="rect3384"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ ry="4.7797003"
+ y="163.79405"
+ x="469.8172"
+ height="64"
+ width="63.461262"
+ id="rect3386"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ ry="4.7797003"
+ y="163.79405"
+ x="768.48407"
+ height="64"
+ width="63.461262"
+ id="rect3388"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3392"
+ width="63.461262"
+ height="64"
+ x="693.82019"
+ y="163.79405"
+ ry="4.7797003" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="99.751244"
+ y="187.30594"
+ id="text3444"><tspan
+ sodipodi:role="line"
+ id="tspan3446"
+ x="99.751244"
+ y="187.30594"
+ style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">Q</tspan></text>
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect2739"
+ width="63.461262"
+ height="64"
+ x="320.05182"
+ y="163.79405"
+ ry="4.7797003" />
+ <text
+ id="text3448"
+ y="187.60356"
+ x="323.15463"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="187.60356"
+ x="323.15463"
+ id="tspan3450"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">R</tspan></text>
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect7301"
+ width="63.461262"
+ height="64"
+ x="171.15358"
+ y="163.79405"
+ ry="4.7797003" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 171.24757,196.86071 c 21.0911,0 42.18218,0 63.27327,0"
+ id="path7305"
+ inkscape:connector-curvature="0" />
+ <text
+ id="text3464"
+ y="187.92427"
+ x="175.64062"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="187.92427"
+ x="175.64062"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ id="tspan3498">W</tspan></text>
+ <text
+ id="text3468"
+ y="216.29616"
+ x="99.515625"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="216.29616"
+ x="99.515625"
+ id="tspan3470"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">q</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="175.64062"
+ y="220.43825"
+ id="text3472"><tspan
+ sodipodi:role="line"
+ id="tspan3474"
+ x="175.64062"
+ y="220.43825"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">w</tspan></text>
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 96.637962,195.79405 c 21.265388,0 42.530768,0 63.796148,0"
+ id="path3476"
+ inkscape:connector-curvature="0" />
+ <g
+ id="g7271"
+ transform="translate(0,16.59406)"
+ style="fill:#eeeeec;fill-opacity:1">
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3378"
+ width="63.461262"
+ height="64"
+ x="395.15076"
+ y="147.2"
+ ry="4.7797003" />
+ <rect
+ ry="2.4296808"
+ y="147.2"
+ x="395.15076"
+ height="32.533333"
+ width="63.461262"
+ id="rect7269"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <path
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;fill-rule:evenodd;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 395.24997,179.73333 c 21.26539,0 42.53077,0 63.79615,0"
+ id="path3484"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 469.91423,196.86071 c 21.08993,0 42.17982,0 63.26974,0"
+ id="path3486"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 544.58331,196.86071 c 21.26538,0 42.53076,0 63.79615,0"
+ id="path3488"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3490"
+ d="m 619.24757,196.32738 c 21.0911,0 42.18218,0 63.27327,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3492"
+ d="m 693.91424,196.86071 c 21.09109,0 42.18218,0 63.27327,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3494"
+ d="m 768.58331,196.86071 c 21.26538,0 42.53076,0 63.79615,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <rect
+ ry="4.7797003"
+ y="163.79405"
+ x="245.38516"
+ height="64"
+ width="63.461262"
+ id="rect2735"
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <path
+ id="path2737"
+ d="m 245.47916,195.19999 c 21.09109,0 42.18217,0 63.27327,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="248.78169"
+ y="187.60356"
+ id="text3502"><tspan
+ sodipodi:role="line"
+ id="tspan3504"
+ x="248.78169"
+ y="187.60356"
+ style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">E</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="248.64069"
+ y="220.16637"
+ id="text3506"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3508"
+ x="248.64069"
+ y="220.16637">e</tspan></text>
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 320.14582,195.19999 c 21.0911,0 42.18218,0 63.27327,0"
+ id="path2741"
+ inkscape:connector-curvature="0" />
+ <text
+ id="text3510"
+ y="219.88054"
+ x="321.97186"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ y="219.88054"
+ x="321.97186"
+ id="tspan3512"
+ sodipodi:role="line">r</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="399.83853"
+ y="220.32741"
+ id="text3532"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3534"
+ x="399.83853"
+ y="220.32741">t</tspan></text>
+ <text
+ id="text3536"
+ y="217.93365"
+ x="474.22501"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="217.93365"
+ x="474.22501"
+ id="tspan3538"
+ sodipodi:role="line">y</tspan></text>
+ <text
+ id="text3540"
+ y="219.90315"
+ x="547.44312"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="219.90315"
+ x="547.44312"
+ id="tspan3542"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">u</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="622.33124"
+ y="220.32741"
+ id="text3544"><tspan
+ sodipodi:role="line"
+ id="tspan3546"
+ x="622.33124"
+ y="220.32741"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">i</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="698.52502"
+ y="219.88054"
+ id="text3548"><tspan
+ sodipodi:role="line"
+ id="tspan3550"
+ x="698.52502"
+ y="219.88054"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">o</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="772.01251"
+ y="219.88054"
+ id="text3552"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3554"
+ x="772.01251"
+ y="219.88054">p</tspan></text>
+ <text
+ id="text3556"
+ y="201.17621"
+ x="851.88257"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ xml:space="preserve"><tspan
+ style="font-size:23.46666718px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672px"
+ y="201.17621"
+ x="851.88257"
+ id="tspan3558"
+ sodipodi:role="line"> </tspan></text>
+ <rect
+ ry="4.7797003"
+ y="238.46074"
+ x="106.23158"
+ height="64"
+ width="64"
+ id="rect3616"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3618"
+ width="63.461262"
+ height="64"
+ x="181.38516"
+ y="238.46074"
+ ry="4.7797003" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3620"
+ width="64.538773"
+ height="64"
+ x="255.56491"
+ y="238.46074"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="238.46074"
+ x="332.05182"
+ height="64"
+ width="63.461262"
+ id="rect3622"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3624"
+ width="63.461262"
+ height="64"
+ x="405.38232"
+ y="238.46074"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="238.46074"
+ x="554.71564"
+ height="64"
+ width="63.461262"
+ id="rect3626"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3628"
+ width="63.461262"
+ height="64"
+ x="629.38507"
+ y="238.46074"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="238.46074"
+ x="779.1535"
+ height="64"
+ width="63.948242"
+ id="rect3634"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <text
+ id="text3638"
+ y="262.07977"
+ x="110.55427"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="262.07977"
+ x="110.55427"
+ id="tspan3640"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">A</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="335.79037"
+ y="264.53333"
+ id="text3642"><tspan
+ sodipodi:role="line"
+ id="tspan3644"
+ x="335.79037"
+ y="264.53333"
+ style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">F</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:monospace;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="185.38518"
+ y="261.80792"
+ id="text3646"><tspan
+ id="tspan3648"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ x="185.38518"
+ y="261.80792">S</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="110.07188"
+ y="295.08054"
+ id="text3650"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3652"
+ x="110.07188"
+ y="295.08054">a</tspan></text>
+ <text
+ id="text3654"
+ y="295.08054"
+ x="185.54375"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="295.08054"
+ x="185.54375"
+ id="tspan3656"
+ sodipodi:role="line">s</tspan></text>
+ <path
+ id="path3658"
+ d="m 106.86955,270.46071 c 21.26538,0 42.53076,0 63.79615,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 181.47916,270.99405 c 21.09109,0 42.18217,0 63.27327,0"
+ id="path3660"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:0.60877639px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 256.71172,270.08518 c 20.26767,0 40.53532,0 60.80299,0"
+ id="path3662"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3664"
+ d="m 330.81249,271.52738 c 21.09109,0 42.18218,0 63.27327,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 405.48156,270.99405 c 21.26538,0 42.53076,0 63.79615,0"
+ id="path3666"
+ inkscape:connector-curvature="0" />
+ <g
+ id="g10542"
+ transform="matrix(1,0,0,-1,0,653.98815)"
+ style="fill:#eeeeec;fill-opacity:1">
+ <rect
+ ry="4.7797003"
+ y="351.5274"
+ x="480.04877"
+ height="64"
+ width="63.461262"
+ id="rect3630"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect8437"
+ width="63.461262"
+ height="33.005924"
+ x="480.04877"
+ y="351.5274"
+ ry="2.4649756" />
+ <path
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 480.14581,384.59406 c 21.08993,0 42.17982,0 63.26974,0"
+ id="path3668"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ id="path3670"
+ d="m 554.81489,271.52738 c 21.26539,0 42.53076,0 63.79615,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 629.47916,270.99405 c 21.09109,0 42.18217,0 63.27327,0"
+ id="path3672"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 704.14582,271.52738 c 21.0911,0 42.18218,0 63.27327,0"
+ id="path3674"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3678"
+ d="m 779.24757,270.99405 c 21.0911,0 42.18218,0 63.27327,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <text
+ id="text3680"
+ y="262.07977"
+ x="259.86594"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="262.07977"
+ x="259.86594"
+ id="tspan3682"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;stroke-width:1.06666672">D</tspan></text>
+ <text
+ id="text3684"
+ y="295.7149"
+ x="260.63126"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="295.7149"
+ x="260.63126"
+ id="tspan3686"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">d</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="335.79037"
+ y="293.96075"
+ id="text3688"><tspan
+ sodipodi:role="line"
+ id="tspan3690"
+ x="335.79037"
+ y="293.96075"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">f</tspan></text>
+ <text
+ id="text3692"
+ y="293.27176"
+ x="409.74942"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="293.27176"
+ x="409.74942"
+ id="tspan3694"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">g</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="483.63565"
+ y="292.44882"
+ id="text3696"><tspan
+ sodipodi:role="line"
+ id="tspan3698"
+ x="483.63565"
+ y="292.44882"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">h</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="563.19849"
+ y="293.46237"
+ id="text3700"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3702"
+ x="563.19849"
+ y="293.46237">j</tspan></text>
+ <text
+ id="text3704"
+ y="298.28571"
+ x="630.01605"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ y="298.28571"
+ x="630.01605"
+ id="tspan3706"
+ sodipodi:role="line">k</tspan></text>
+ <g
+ id="g10537"
+ transform="translate(0,-112.47259)"
+ style="fill:#eeeeec;fill-opacity:1">
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect8447"
+ width="63.461262"
+ height="64"
+ x="704"
+ y="350.93332"
+ ry="4.7797003" />
+ <rect
+ ry="2.3898501"
+ y="382.93332"
+ x="704"
+ height="32"
+ width="63.461262"
+ id="rect8449"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <path
+ id="path8451"
+ d="m 704.33984,383.97137 c 21.08993,0 42.17982,0 63.26974,0"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ </g>
+ <text
+ id="text3708"
+ y="297.45032"
+ x="708.60419"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ y="297.45032"
+ x="708.60419"
+ id="tspan3710"
+ sodipodi:role="line">l</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="781.47919"
+ y="292.20032"
+ id="text3716"><tspan
+ sodipodi:role="line"
+ id="tspan3718"
+ x="781.47919"
+ y="292.20032"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">;</tspan></text>
+ <rect
+ ry="4.7797003"
+ y="238.46074"
+ x="854.2525"
+ height="64"
+ width="63.461262"
+ id="rect3720"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <path
+ id="path3724"
+ d="m 854.35172,271.52738 c 21.26539,0 42.53077,0 63.79615,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <rect
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3722"
+ width="137.60541"
+ height="64"
+ x="929.06122"
+ y="238.46074"
+ ry="4.7797003" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="858.5"
+ y="296.23157"
+ id="text3728"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3730"
+ x="858.5"
+ y="296.23157">'</tspan></text>
+ <text
+ id="text3740"
+ y="279.57574"
+ x="929.25153"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:'Bitstream Vera Sans';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="279.57574"
+ x="929.25153"
+ id="tspan3742"
+ sodipodi:role="line"
+ style="font-size:21.33333397px;line-height:1.25;stroke-width:1.06666672">↲</tspan></text>
+ <rect
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3764"
+ width="64"
+ height="64"
+ x="116.89825"
+ y="313.12741"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="313.12741"
+ x="192.05182"
+ height="64"
+ width="63.461262"
+ id="rect3766"
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3770"
+ width="63.461262"
+ height="64"
+ x="341.38516"
+ y="313.12741"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="313.12741"
+ x="416.04898"
+ height="64"
+ width="63.461262"
+ id="rect3772"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ ry="4.7797003"
+ y="313.12741"
+ x="490.71542"
+ height="64"
+ width="63.461262"
+ id="rect3778"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3780"
+ width="63.948242"
+ height="64"
+ x="789.82019"
+ y="313.12741"
+ ry="4.7797003" />
+ <text
+ id="text3796"
+ y="368.70032"
+ x="126.76303"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="368.70032"
+ x="126.76303"
+ id="tspan3798"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">z</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="199.07387"
+ y="367.11551"
+ id="text3800"><tspan
+ sodipodi:role="line"
+ id="tspan3802"
+ x="199.07387"
+ y="367.11551"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">x</tspan></text>
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 117.53621,345.12738 c 21.26539,0 42.53077,0 63.79615,0"
+ id="path3804"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3806"
+ d="m 192.14582,345.66071 c 21.0911,0 42.18218,0 63.27327,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <rect
+ ry="4.7797003"
+ y="313.12741"
+ x="266.23157"
+ height="64"
+ width="64.538773"
+ id="rect3768"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <path
+ id="path3808"
+ d="m 267.37839,344.75185 c 20.26767,0 40.53532,0 60.80299,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.06666672;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 341.47916,346.19405 c 21.09109,0 42.18217,0 63.27327,0"
+ id="path3810"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3812"
+ d="m 416.14822,345.66071 c 21.26539,0 42.53077,0 63.79615,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 490.81248,346.19405 c 21.08993,0 42.17982,0 63.26974,0"
+ id="path3814"
+ inkscape:connector-curvature="0" />
+ <g
+ id="g7140"
+ transform="translate(0,16.594076)"
+ style="fill:#eeeeec;fill-opacity:1">
+ <rect
+ ry="4.7797003"
+ y="296.53333"
+ x="565.38226"
+ height="64"
+ width="63.461262"
+ id="rect3774"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect7131"
+ width="63.461262"
+ height="32"
+ x="565.38226"
+ y="328.53333"
+ ry="2.3898501" />
+ <path
+ id="path3816"
+ d="m 565.48156,329.6 c 21.26538,0 42.53076,0 63.79615,0"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ </g>
+ <g
+ id="g7162"
+ transform="translate(0,16.594076)"
+ style="fill:#babdb6;fill-opacity:1">
+ <rect
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3776"
+ width="63.461262"
+ height="64"
+ x="640.05176"
+ y="296.53333"
+ ry="4.7797003" />
+ <rect
+ ry="2.3898501"
+ y="328.53333"
+ x="640.05176"
+ height="32"
+ width="63.461262"
+ id="rect7160"
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <path
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 640.14582,329.06667 c 21.0911,0 42.18218,0 63.27327,0"
+ id="path3818"
+ inkscape:connector-curvature="0" />
+ </g>
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 789.91424,345.66071 c 21.09109,0 42.18218,0 63.27327,0"
+ id="path3822"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="271.65314"
+ y="368.61978"
+ id="text3828"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3830"
+ x="271.65314"
+ y="368.61978">c</tspan></text>
+ <text
+ id="text3832"
+ y="368.62741"
+ x="348.59036"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="368.62741"
+ x="348.59036"
+ id="tspan3834"
+ sodipodi:role="line">v</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="420.34647"
+ y="367.93842"
+ id="text3836"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3838"
+ x="420.34647"
+ y="367.93842">b</tspan></text>
+ <text
+ id="text3840"
+ y="368.05829"
+ x="494.32407"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ y="368.05829"
+ x="494.32407"
+ id="tspan3842"
+ sodipodi:role="line">n</tspan></text>
+ <text
+ id="text3844"
+ y="368.12903"
+ x="573.86517"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="368.12903"
+ x="573.86517"
+ id="tspan3846"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">m</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="643.82837"
+ y="367.86551"
+ id="text3848"><tspan
+ sodipodi:role="line"
+ id="tspan3850"
+ x="643.82837"
+ y="367.86551"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">,</tspan></text>
+ <g
+ id="g7167"
+ transform="translate(74.666662,16.594076)"
+ style="fill:#eeeeec;fill-opacity:1">
+ <rect
+ ry="4.7797003"
+ y="296.53333"
+ x="640.05176"
+ height="64"
+ width="63.461262"
+ id="rect7169"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect7171"
+ width="63.461262"
+ height="32"
+ x="640.05176"
+ y="328.53333"
+ ry="2.3898501" />
+ <path
+ id="path7173"
+ d="m 640.14582,329.06667 c 21.0911,0 42.18218,0 63.27327,0"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="718.6156"
+ y="367.46667"
+ id="text3852"><tspan
+ sodipodi:role="line"
+ id="tspan3854"
+ x="718.6156"
+ y="367.46667"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">.</tspan></text>
+ <text
+ id="text3856"
+ y="367.10361"
+ x="798.54919"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="367.10361"
+ x="798.54919"
+ id="tspan3858"
+ sodipodi:role="line">/</tspan></text>
+ <rect
+ ry="4.7797003"
+ y="89.127388"
+ x="85.333328"
+ height="64"
+ width="64"
+ id="rect3876"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3878"
+ width="63.461262"
+ height="64"
+ x="160.48691"
+ y="89.127388"
+ ry="4.7797003" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect10511"
+ width="63.461262"
+ height="64"
+ x="235.15358"
+ y="89.127388"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="89.127388"
+ x="309.82025"
+ height="64"
+ width="63.461262"
+ id="rect3882"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3884"
+ width="63.461262"
+ height="64"
+ x="384.48407"
+ y="89.127388"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="89.127388"
+ x="533.81738"
+ height="64"
+ width="63.461262"
+ id="rect3886"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3888"
+ width="63.461262"
+ height="64"
+ x="608.48688"
+ y="89.127388"
+ ry="4.7797003" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3890"
+ width="63.461262"
+ height="64"
+ x="459.15051"
+ y="89.127388"
+ ry="4.7797003" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3892"
+ width="63.461262"
+ height="64"
+ x="757.33331"
+ y="89.127388"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="89.127388"
+ x="832.48688"
+ height="64"
+ width="63.461262"
+ id="rect3894"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ ry="4.7797003"
+ y="89.127388"
+ x="683.1535"
+ height="64"
+ width="63.461262"
+ id="rect3896"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="88.721916"
+ y="145.3714"
+ id="text3910"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3912"
+ x="88.721916"
+ y="145.3714">1</tspan></text>
+ <text
+ id="text3914"
+ y="144.91666"
+ x="162.87187"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="144.91666"
+ x="162.87187"
+ id="tspan3916"
+ sodipodi:role="line">2</tspan></text>
+ <path
+ id="path3918"
+ d="m 85.971291,121.12738 c 21.265389,0 42.530769,0 63.796159,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 160.58091,121.66071 c 21.09109,0 42.18217,0 63.27327,0"
+ id="path3920"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3924"
+ d="m 309.91424,122.19405 c 21.09109,0 42.18218,0 63.27327,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 384.58331,121.66071 c 21.26538,0 42.53076,0 63.79615,0"
+ id="path3926"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3928"
+ d="m 459.24756,122.19405 c 21.08993,0 42.17982,0 63.26974,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.1617893px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3930"
+ d="m 533.91664,122.19405 c 21.26539,0 42.53076,0 63.79615,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 608.58091,121.66071 c 21.09109,0 42.18217,0 63.27327,0"
+ id="path3932"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 683.24757,122.19405 c 21.0911,0 42.18218,0 63.27327,0"
+ id="path3934"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 757.91664,122.19405 c 21.26539,0 42.53076,0 63.79615,0"
+ id="path3936"
+ inkscape:connector-curvature="0" />
+ <path
+ id="path3938"
+ d="m 832.58091,121.66071 c 21.09109,0 42.18217,0 63.27327,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 235.24757,122.19405 c 21.0911,0 42.18218,0 63.27327,0"
+ id="path10513"
+ inkscape:connector-curvature="0" />
+ <text
+ id="text3944"
+ y="144.91666"
+ x="239.48438"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="144.91666"
+ x="239.48438"
+ id="tspan3946"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">3</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="312.26355"
+ y="145.3714"
+ id="text3948"><tspan
+ sodipodi:role="line"
+ id="tspan3950"
+ x="312.26355"
+ y="145.3714"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">4</tspan></text>
+ <text
+ id="text3952"
+ y="144.79477"
+ x="386.92606"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="144.79477"
+ x="386.92606"
+ id="tspan3954"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">5</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="461.15417"
+ y="144.91666"
+ id="text3956"><tspan
+ sodipodi:role="line"
+ id="tspan3958"
+ x="461.15417"
+ y="144.91666"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">6</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="536.29688"
+ y="145.06665"
+ id="text3960"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3962"
+ x="536.29688"
+ y="145.06665">7</tspan></text>
+ <text
+ id="text3964"
+ y="144.79477"
+ x="611.03851"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="144.79477"
+ x="611.03851"
+ id="tspan3966"
+ sodipodi:role="line">8</tspan></text>
+ <text
+ id="text3968"
+ y="144.82977"
+ x="686.55652"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="144.82977"
+ x="686.55652"
+ id="tspan3970"
+ sodipodi:role="line">9</tspan></text>
+ <text
+ id="text3972"
+ y="144.57379"
+ x="760.35419"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="144.57379"
+ x="760.35419"
+ id="tspan3974"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">0</tspan></text>
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3976"
+ width="63.461262"
+ height="64"
+ x="906.71844"
+ y="89.127388"
+ ry="4.7797003" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 906.81249,121.66071 c 21.09109,0 42.18218,0 63.27327,0"
+ id="path3978"
+ inkscape:connector-curvature="0" />
+ <rect
+ ry="4.7797003"
+ y="89.127388"
+ x="11.153512"
+ height="64"
+ width="63.461262"
+ id="rect3980"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <path
+ id="path3982"
+ d="m 11.247578,121.66071 c 21.091093,0 42.182176,0 63.273269,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="840.27075"
+ y="143.8774"
+ id="text3988"><tspan
+ sodipodi:role="line"
+ id="tspan3990"
+ x="840.27075"
+ y="143.8774"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">-</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="913.50781"
+ y="144.89095"
+ id="text3992"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3994"
+ x="913.50781"
+ y="144.89095">=</tspan></text>
+ <rect
+ ry="4.7797003"
+ y="89.127388"
+ x="981.67877"
+ height="64"
+ width="95.078056"
+ id="rect3996"
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <text
+ id="text4004"
+ y="141.20207"
+ x="992.40417"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="141.20207"
+ x="992.40417"
+ id="tspan4006"
+ sodipodi:role="line"
+ style="font-size:13.86666679px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">Backspace</tspan></text>
+ <text
+ id="text4977"
+ y="117.46353"
+ x="89.259377"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="117.46353"
+ x="89.259377"
+ id="tspan4979"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">!</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="162.17561"
+ y="116.44883"
+ id="text4981"><tspan
+ sodipodi:role="line"
+ id="tspan4983"
+ x="162.17561"
+ y="116.44883"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">@</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="238.91458"
+ y="117.86665"
+ id="text4985"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan4987"
+ x="238.91458"
+ y="117.86665">#</tspan></text>
+ <text
+ id="text4989"
+ y="117.96072"
+ x="311.69211"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="117.96072"
+ x="311.69211"
+ id="tspan4991"
+ sodipodi:role="line">$</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="387.71487"
+ y="117.27174"
+ id="text4993"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan4995"
+ x="387.71487"
+ y="117.27174">%</tspan></text>
+ <text
+ id="text4997"
+ y="116.44883"
+ x="461.6011"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="116.44883"
+ x="461.6011"
+ id="tspan4999"
+ sodipodi:role="line">^</tspan></text>
+ <text
+ id="text5001"
+ y="117.46236"
+ x="536.96692"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="117.46236"
+ x="536.96692"
+ id="tspan5003"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">&amp;</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="609.5968"
+ y="117.19882"
+ id="text5005"><tspan
+ sodipodi:role="line"
+ id="tspan5007"
+ x="609.5968"
+ y="117.19882"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">*</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="685.03265"
+ y="116.44881"
+ id="text5009"><tspan
+ sodipodi:role="line"
+ id="tspan5011"
+ x="685.03265"
+ y="116.44881"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">(</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="761.91931"
+ y="116.12902"
+ id="text5013"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan5015"
+ x="761.91931"
+ y="116.12902">)</tspan></text>
+ <rect
+ ry="3.5688117"
+ y="387.40637"
+ x="254.19489"
+ height="47.786243"
+ width="385.80511"
+ id="rect5017"
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <g
+ id="g4061"
+ transform="translate(0,-7.1714294)">
+ <text
+ id="text5021"
+ y="422.17459"
+ x="261.68033"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="422.17459"
+ x="261.68033"
+ id="tspan5023"
+ sodipodi:role="line"
+ style="font-size:13.86666679px;line-height:1.25;stroke-width:1.06666672">Space</tspan></text>
+ </g>
+ <text
+ id="text6971"
+ y="339.18835"
+ x="125.368"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="339.18835"
+ x="125.368"
+ id="tspan6973"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">Z</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="197.67885"
+ y="337.60355"
+ id="text6975"><tspan
+ sodipodi:role="line"
+ id="tspan6977"
+ x="197.67885"
+ y="337.60355"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">X</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="271.76355"
+ y="339.99478"
+ id="text6979"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan6981"
+ x="271.76355"
+ y="339.99478">C</tspan></text>
+ <text
+ id="text6983"
+ y="339.11542"
+ x="347.19534"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="339.11542"
+ x="347.19534"
+ id="tspan6985"
+ sodipodi:role="line">V</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="418.95145"
+ y="338.42645"
+ id="text6987"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan6989"
+ x="418.95145"
+ y="338.42645">B</tspan></text>
+ <text
+ id="text6991"
+ y="338.27698"
+ x="494.1412"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ y="338.27698"
+ x="494.1412"
+ id="tspan6993"
+ sodipodi:role="line">N</tspan></text>
+ <text
+ id="text6995"
+ y="338.6171"
+ x="572.47015"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="338.6171"
+ x="572.47015"
+ id="tspan6997"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">M</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="645.10004"
+ y="338.35355"
+ id="text6999"><tspan
+ sodipodi:role="line"
+ id="tspan7001"
+ x="645.10004"
+ y="338.35355"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">&lt;</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="723.39313"
+ y="336.84164"
+ id="text7003"><tspan
+ sodipodi:role="line"
+ id="tspan7005"
+ x="723.39313"
+ y="336.84164"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">&gt;</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="798.75592"
+ y="337.85516"
+ id="text7007"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7009"
+ x="798.75592"
+ y="337.85516">?</tspan></text>
+ <text
+ id="text7027"
+ y="264.07922"
+ x="409.74942"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="264.07922"
+ x="409.74942"
+ id="tspan7029"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">G</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="483.63565"
+ y="263.25632"
+ id="text7031"><tspan
+ sodipodi:role="line"
+ id="tspan7033"
+ x="483.63565"
+ y="263.25632"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672">H</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="559.00153"
+ y="264.26987"
+ id="text7035"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7037"
+ x="559.00153"
+ y="264.26987">J</tspan></text>
+ <text
+ id="text7039"
+ y="263.13974"
+ x="631.41089"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ y="263.13974"
+ x="631.41089"
+ id="tspan7041"
+ sodipodi:role="line">K</tspan></text>
+ <text
+ id="text7043"
+ y="264.96075"
+ x="708.60419"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ y="264.96075"
+ x="708.60419"
+ id="tspan7045"
+ sodipodi:role="line">L</tspan></text>
+ <text
+ id="text7047"
+ y="263.34451"
+ x="781.32269"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="263.34451"
+ x="781.32269"
+ id="tspan7049"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">:</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="399.83853"
+ y="187.92427"
+ id="text7051"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7053"
+ x="399.83853"
+ y="187.92427">T</tspan></text>
+ <text
+ id="text7055"
+ y="187.60356"
+ x="474.54233"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="187.60356"
+ x="474.54233"
+ id="tspan7057"
+ sodipodi:role="line">Y</tspan></text>
+ <text
+ id="text7059"
+ y="187.3317"
+ x="548.19391"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="187.3317"
+ x="548.19391"
+ id="tspan7061"
+ sodipodi:role="line"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">U</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="621.77612"
+ y="187.60356"
+ id="text7063"><tspan
+ sodipodi:role="line"
+ id="tspan7065"
+ x="621.77612"
+ y="187.60356"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">I</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="698.92633"
+ y="187.3317"
+ id="text7067"><tspan
+ sodipodi:role="line"
+ id="tspan7069"
+ x="698.92633"
+ y="187.3317"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">O</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="771.70312"
+ y="187.92427"
+ id="text7071"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7073"
+ x="771.70312"
+ y="187.92427">P</tspan></text>
+ <rect
+ ry="4.7797003"
+ y="163.79405"
+ x="842.71564"
+ height="64"
+ width="63.461262"
+ id="rect7075"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect7077"
+ width="63.461262"
+ height="64"
+ x="917.38507"
+ y="163.79405"
+ ry="4.7797003" />
+ <rect
+ ry="4.7797003"
+ y="163.79405"
+ x="992.05176"
+ height="64"
+ width="85.281578"
+ id="rect7079"
+ style="font-size:18px;fill:#babdb6;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <path
+ id="path7081"
+ d="m 842.81489,196.86071 c 21.26539,0 42.53076,0 63.79615,0"
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16661239px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.16182172px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 917.47916,196.32738 c 21.09109,0 42.18217,0 63.27327,0"
+ id="path7083"
+ inkscape:connector-curvature="0" />
+ <path
+ style="font-size:18px;fill:none;stroke:#000000;stroke-width:1.34042335px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
+ d="m 992.33815,196.86071 c 28.07395,0 56.14795,0 84.22195,0"
+ id="path7085"
+ inkscape:connector-curvature="0" />
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="845.2594"
+ y="219.39616"
+ id="text7087"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7089"
+ x="845.2594"
+ y="219.39616">[</tspan></text>
+ <text
+ id="text7091"
+ y="219.39616"
+ x="919.95624"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="219.39616"
+ x="919.95624"
+ id="tspan7093"
+ sodipodi:role="line">]</tspan></text>
+ <text
+ id="text7095"
+ y="219.92429"
+ x="993.08435"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="219.92429"
+ x="993.08435"
+ id="tspan7097"
+ sodipodi:role="line">\</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="844.91248"
+ y="186.91489"
+ id="text7099"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7101"
+ x="844.91248"
+ y="186.91489">{</tspan></text>
+ <text
+ id="text7103"
+ y="186.91489"
+ x="918.51251"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="186.91489"
+ x="918.51251"
+ id="tspan7105"
+ sodipodi:role="line">}</tspan></text>
+ <text
+ id="text7107"
+ y="185.39926"
+ x="993.43127"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ y="185.39926"
+ x="993.43127"
+ id="tspan7109"
+ sodipodi:role="line">|</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot7111"
+ style="font-size:12.80000019px;line-height:0.01%;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"><flowRegion
+ id="flowRegion7113"
+ style="stroke-width:1.06666672"><rect
+ id="rect7115"
+ width="12.929953"
+ height="17.239937"
+ x="859.84186"
+ y="230.24284"
+ style="font-size:21.33333397px;font-family:'DejaVu Sans Mono';stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara7117"
+ style="font-size:21.33333397px;line-height:1.25;stroke-width:1.06666672">'&quot;l</flowPara></flowRoot> <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="857.28125"
+ y="264.82532"
+ id="text7119"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7121"
+ x="857.28125"
+ y="264.82532">&quot;</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="20.090919"
+ y="145.65862"
+ id="text7123"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7125"
+ x="20.090919"
+ y="145.65862">`</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="18.695885"
+ y="116.14663"
+ id="text7127"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7129"
+ x="18.695885"
+ y="116.14663">~</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="338.80719"
+ y="208.43478"
+ id="text7245"><tspan
+ sodipodi:role="line"
+ x="338.80719"
+ y="208.43478"
+ id="tspan7366"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="338.80719"
+ y="216.3177"
+ id="tspan7249"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">reload</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot7342"
+ style="font-size:12.80000019px;line-height:0.01%;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"><flowRegion
+ id="flowRegion7344"
+ style="stroke-width:1.06666672"><rect
+ id="rect7346"
+ width="17.453804"
+ height="36.448208"
+ x="14.546197"
+ y="174.75179"
+ style="font-size:13.86666679px;font-family:'DejaVu Sans Mono';stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara7348"
+ style="font-size:13.86666679px;line-height:1.25;stroke-width:1.06666672">TabTa</flowPara></flowRoot> <text
+ id="text7358"
+ y="199.05951"
+ x="11.713984"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="199.05951"
+ x="11.713984"
+ id="tspan7360"
+ sodipodi:role="line"
+ style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">Tab</tspan></text>
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect10890"
+ width="338.15115"
+ height="41.734409"
+ x="101.31554"
+ y="20.999462"
+ ry="3.116843" />
+ <text
+ id="text7404"
+ y="49.597778"
+ x="106.28438"
+ style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:condensed;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans';text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="49.597778"
+ x="106.28438"
+ id="tspan7406"
+ sodipodi:role="line"
+ style="font-size:23.46666718px;line-height:1;stroke-width:1.06666672">qutebrowser default bindings</tspan></text>
+ <text
+ id="text9514"
+ y="279.05405"
+ x="578.2074"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="279.05405"
+ x="578.2074"
+ sodipodi:role="line"
+ id="tspan9524"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="286.93695"
+ x="578.2074"
+ sodipodi:role="line"
+ id="tspan4938"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan
+ y="294.61697"
+ x="578.2074"
+ sodipodi:role="line"
+ id="tspan4936"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">down</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="650.2074"
+ y="279.24451"
+ id="text9526"><tspan
+ sodipodi:role="line"
+ x="650.2074"
+ y="279.24451"
+ id="tspan9530"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="650.2074"
+ y="286.9245"
+ id="tspan4942"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan
+ id="tspan9532"
+ sodipodi:role="line"
+ x="650.2074"
+ y="294.60449"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">up</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="578.2074"
+ y="247.81595"
+ id="text10515"><tspan
+ sodipodi:role="line"
+ x="578.2074"
+ y="247.81595"
+ id="tspan10517"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="578.2074"
+ y="255.69887"
+ id="tspan10519"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">next</tspan><tspan
+ id="tspan10521"
+ sodipodi:role="line"
+ x="578.2074"
+ y="263.37888"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text>
+ <text
+ id="text10523"
+ y="248.00641"
+ x="650.2074"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10525"
+ y="248.00641"
+ x="650.2074"
+ sodipodi:role="line"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ id="tspan10527"
+ y="255.88933"
+ x="650.2074"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">previous</tspan><tspan
+ y="263.56934"
+ x="650.2074"
+ sodipodi:role="line"
+ id="tspan10529"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="501.71222"
+ y="278.67307"
+ id="text10547"><tspan
+ sodipodi:role="line"
+ x="501.71222"
+ y="278.67307"
+ id="tspan10560"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="501.71222"
+ y="286.55597"
+ id="tspan10574"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan
+ sodipodi:role="line"
+ x="501.71222"
+ y="294.23599"
+ id="tspan10562"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">left</tspan></text>
+ <text
+ id="text10564"
+ y="278.67307"
+ x="724.62445"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="278.67307"
+ x="724.62445"
+ sodipodi:role="line"
+ id="tspan10566"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ id="tspan10568"
+ y="286.55597"
+ x="724.62445"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan
+ id="tspan10570"
+ y="294.23599"
+ x="724.62445"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">right</tspan></text>
+ <text
+ id="text10564-6"
+ y="199.11761"
+ x="715.61145"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10570-9"
+ y="199.11761"
+ x="715.61145"
+ sodipodi:role="line"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="207.75761"
+ x="715.61145"
+ sodipodi:role="line"
+ id="tspan4975"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="215.64053"
+ x="715.61145"
+ sodipodi:role="line"
+ id="tspan4977"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">open <tspan
+ style="fill:#ff0000;stroke-width:1.06666672"
+ id="tspan3697">(6)</tspan></tspan></text>
+ <text
+ id="text10564-3"
+ y="166.45094"
+ x="714.94476"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10570-6"
+ y="166.45094"
+ x="714.94476"
+ sodipodi:role="line"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="174.33386"
+ x="714.94476"
+ sodipodi:role="line"
+ id="tspan4996"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">open in</tspan><tspan
+ y="182.01385"
+ x="714.94476"
+ sodipodi:role="line"
+ id="tspan4998"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">new tab<tspan
+ style="fill:#ff0000;stroke-width:1.06666672"
+ id="tspan3699" /></tspan><tspan
+ y="189.69386"
+ x="714.94476"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan3701"><tspan
+ style="fill:#ff0000;stroke-width:1.06666672"
+ id="tspan3703">(6)</tspan></tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="282.57269"
+ y="279.80652"
+ id="text10547-2"><tspan
+ sodipodi:role="line"
+ x="282.57269"
+ y="279.80652"
+ id="tspan10560-6"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="282.57269"
+ y="287.68942"
+ id="tspan10562-1"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">close</tspan><tspan
+ sodipodi:role="line"
+ x="282.57269"
+ y="295.36945"
+ id="tspan5970"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="351.18723"
+ y="272.2189"
+ id="text10547-2-1"><tspan
+ sodipodi:role="line"
+ x="351.18723"
+ y="272.2189"
+ id="tspan10560-6-1"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="351.18723"
+ y="280.10181"
+ id="tspan5062"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">hint</tspan><tspan
+ sodipodi:role="line"
+ x="351.18723"
+ y="287.78183"
+ id="tspan5066"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">(label</tspan><tspan
+ sodipodi:role="line"
+ x="351.18723"
+ y="295.46182"
+ id="tspan6140"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">links) <tspan
+ style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan3723">(8)</tspan></tspan></text>
+ <text
+ id="text9514-6"
+ y="201.01891"
+ x="564.76721"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="201.01891"
+ x="564.76721"
+ sodipodi:role="line"
+ id="tspan9524-9"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="208.90182"
+ x="564.76721"
+ sodipodi:role="line"
+ id="tspan4936-0"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">undo</tspan><tspan
+ y="216.58182"
+ x="564.76721"
+ sodipodi:role="line"
+ id="tspan5968"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">closing</tspan><tspan
+ y="224.26183"
+ x="564.76721"
+ sodipodi:role="line"
+ id="tspan5436"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text>
+ <text
+ id="text9514-4"
+ y="205.01891"
+ x="638.90051"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="205.01891"
+ x="638.90051"
+ sodipodi:role="line"
+ id="tspan4936-5"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="212.90182"
+ x="638.90051"
+ sodipodi:role="line"
+ id="tspan5132"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">insert-</tspan><tspan
+ y="220.58182"
+ x="638.90051"
+ sodipodi:role="line"
+ id="tspan5136"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mode</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="840.11176"
+ y="108.99891"
+ id="text3988-2"><tspan
+ sodipodi:role="line"
+ id="tspan3990-4"
+ x="840.11176"
+ y="108.99891"
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">_</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#0000ff;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="913.50781"
+ y="110.18444"
+ id="text3992-3"><tspan
+ style="font-size:19.20000076px;line-height:1.25;font-family:'DejaVu Sans Mono';fill:#0000ff;stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan3994-3"
+ x="913.50781"
+ y="110.18444">+</tspan></text>
+ <text
+ id="text9514-6-5"
+ y="129.16545"
+ x="856.87421"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="129.16545"
+ x="856.87421"
+ sodipodi:role="line"
+ id="tspan9524-9-4"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="137.04837"
+ x="856.87421"
+ sodipodi:role="line"
+ id="tspan4936-0-0"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">zoom</tspan><tspan
+ y="144.72836"
+ x="856.87421"
+ sodipodi:role="line"
+ id="tspan6188"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">out</tspan></text>
+ <text
+ id="text9514-6-1"
+ y="96"
+ x="930.13336"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="96"
+ x="930.13336"
+ sodipodi:role="line"
+ id="tspan9524-9-1"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="103.68"
+ x="930.13336"
+ sodipodi:role="line"
+ id="tspan4936-0-6"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">zoom</tspan><tspan
+ y="111.36"
+ x="930.13336"
+ sodipodi:role="line"
+ id="tspan5593"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">in</tspan></text>
+ <text
+ id="text9514-6-1-2"
+ y="133.00302"
+ x="929.7572"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="133.00302"
+ x="929.7572"
+ sodipodi:role="line"
+ id="tspan9524-9-1-6"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">set</tspan><tspan
+ y="140.68301"
+ x="929.7572"
+ sodipodi:role="line"
+ id="tspan5252"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">zoom</tspan><tspan
+ y="148.36302"
+ x="929.7572"
+ sodipodi:role="line"
+ id="tspan4936-0-6-8"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">level</tspan></text>
+ <rect
+ ry="4.7797003"
+ y="14.834106"
+ x="12.374795"
+ height="64"
+ width="63.461262"
+ id="rect3980-8"
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672" />
+ <text
+ id="text7358-4"
+ y="29.305208"
+ x="18.544792"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="29.305208"
+ x="18.544792"
+ id="tspan7360-3"
+ sodipodi:role="line"
+ style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672">Esc</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="351.18723"
+ y="242.80072"
+ id="text10547-2-1-5"><tspan
+ sodipodi:role="line"
+ x="351.18723"
+ y="242.80072"
+ id="tspan10560-6-1-1"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="351.18723"
+ y="250.68364"
+ id="tspan5062-4"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">hint</tspan><tspan
+ sodipodi:role="line"
+ x="351.18723"
+ y="258.36365"
+ id="tspan5066-3"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">(new</tspan><tspan
+ sodipodi:role="line"
+ x="351.18723"
+ y="266.04364"
+ id="tspan6144"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab)</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="501.71222"
+ y="251.41891"
+ id="text10547-23"><tspan
+ sodipodi:role="line"
+ x="501.71222"
+ y="251.41891"
+ id="tspan10560-1"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="501.71222"
+ y="259.30182"
+ id="tspan10562-12"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">back <tspan
+ style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan3586">(7)</tspan></tspan></text>
+ <text
+ id="text10564-6-7"
+ y="239.34317"
+ x="724.62445"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10570-9-4"
+ y="239.34317"
+ x="724.62445"
+ sodipodi:role="line"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="247.98317"
+ x="724.62445"
+ sodipodi:role="line"
+ id="tspan4975-2"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="255.86609"
+ x="724.62445"
+ sodipodi:role="line"
+ id="tspan4977-3"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">forward</tspan><tspan
+ y="263.54608"
+ x="724.62445"
+ sodipodi:role="line"
+ id="tspan3588"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(7)</tspan></text>
+ <text
+ id="text10564-6-7-8"
+ y="347.8989"
+ x="814.39789"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10570-9-4-6"
+ y="347.8989"
+ x="814.39789"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="355.57889"
+ x="814.39789"
+ sodipodi:role="line"
+ id="tspan4975-2-8"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="363.25888"
+ x="814.39789"
+ sodipodi:role="line"
+ id="tspan4977-3-1"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">search</tspan></text>
+ <text
+ id="text10564-6-7-8-3"
+ y="238.93333"
+ x="800"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10570-9-4-6-2"
+ y="238.93333"
+ x="800"
+ sodipodi:role="line"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="247.57333"
+ x="800"
+ sodipodi:role="line"
+ id="tspan4975-2-8-1"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="255.45625"
+ x="800"
+ sodipodi:role="line"
+ id="tspan4977-3-1-5"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">cmd</tspan><tspan
+ y="263.13626"
+ x="800"
+ sodipodi:role="line"
+ id="tspan5435"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mode</tspan></text>
+ <text
+ id="text9514-60"
+ y="353.55225"
+ x="511.70053"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="353.55225"
+ x="511.70053"
+ sodipodi:role="line"
+ id="tspan9524-92"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="361.23224"
+ x="511.70053"
+ sodipodi:role="line"
+ id="tspan4936-8"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">search</tspan><tspan
+ y="368.91223"
+ x="511.70053"
+ sodipodi:role="line"
+ id="tspan5466"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">next</tspan></text>
+ <text
+ id="text9514-60-7"
+ y="319.95224"
+ x="511.99792"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="319.95224"
+ x="511.99792"
+ sodipodi:role="line"
+ id="tspan9524-92-1"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="327.63223"
+ x="511.99792"
+ sodipodi:role="line"
+ id="tspan4936-8-5"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">search</tspan><tspan
+ y="335.31223"
+ x="511.99792"
+ sodipodi:role="line"
+ id="tspan5466-8"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">prev</tspan></text>
+ <text
+ id="text9514-60-8"
+ y="355.28558"
+ x="588.79791"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="355.28558"
+ x="588.79791"
+ sodipodi:role="line"
+ id="tspan5524"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">save</tspan><tspan
+ y="362.96558"
+ x="588.79791"
+ sodipodi:role="line"
+ id="tspan5530"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">quick-</tspan><tspan
+ y="370.64557"
+ x="588.79791"
+ sodipodi:role="line"
+ id="tspan5532"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mark</tspan></text>
+ <text
+ id="text9514-8"
+ y="204.21892"
+ x="490.50494"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="204.21892"
+ x="490.50494"
+ sodipodi:role="line"
+ id="tspan9524-5"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="212.10184"
+ x="490.50494"
+ sodipodi:role="line"
+ id="tspan4938-4"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">yank/</tspan><tspan
+ y="219.78183"
+ x="490.50494"
+ sodipodi:role="line"
+ id="tspan4936-1"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">copy <tspan
+ style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan5713">(1)</tspan></tspan></text>
+ <text
+ id="text9514-8-9"
+ y="206.18428"
+ x="788.66119"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="206.18428"
+ x="788.66119"
+ sodipodi:role="line"
+ id="tspan9524-5-1"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="214.0672"
+ x="788.66119"
+ sodipodi:role="line"
+ id="tspan6096"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">paste</tspan><tspan
+ y="221.74719"
+ x="788.66119"
+ sodipodi:role="line"
+ id="tspan5366"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(2)</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot5590"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"><flowRegion
+ id="flowRegion5592"
+ style="font-family:sans-serif;stroke-width:1.06666672px"><rect
+ id="rect5594"
+ width="552"
+ height="56"
+ x="518.66669"
+ y="25.333334"
+ style="font-size:10.66666698px;font-family:sans-serif;stroke-width:1.13777781px" /></flowRegion><flowPara
+ id="flowPara5596"
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;stroke-width:1.06666672px"> </flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot5598"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5600"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5602"
+ width="582.66669"
+ height="61.333332"
+ x="501.33334"
+ y="14.933333"
+ style="font-size:10.66666698px;font-family:sans-serif;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5604"
+ style="font-size:13.86666679px;line-height:1.25;font-family:sans-serif;stroke-width:1.06666672">Website: https://www.qutebrowser.org/ </flowPara><flowPara
+ id="flowPara5595"
+ style="font-size:13.86666679px;line-height:1.25;font-family:sans-serif;stroke-width:1.06666672">IRC: #qutebrowser on Freenode</flowPara><flowPara
+ id="flowPara5597"
+ style="font-size:13.86666679px;line-height:1.25;font-family:sans-serif;stroke-width:1.06666672">Mailinglist: qutebrowser@lists.qutebrowser.org</flowPara></flowRoot> <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="425.7652"
+ y="241.39365"
+ id="text10547-23-6"><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="241.39365"
+ id="tspan10560-1-3"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="250.03365"
+ id="tspan10574-1-0"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672">s<tspan
+ style="font-size:8.53333378px;stroke-width:1.06666672"
+ id="tspan5591">croll to</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="257.91656"
+ id="tspan5633"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">bottom/</tspan><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="265.59656"
+ id="tspan5461"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">perc.</tspan><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="273.27655"
+ id="tspan10562-12-5"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan></text>
+ <text
+ id="text9514-60-7-7"
+ y="349.15512"
+ x="437.06458"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="349.15512"
+ x="437.06458"
+ sodipodi:role="line"
+ id="tspan9524-92-1-3"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="356.83511"
+ x="437.06458"
+ sodipodi:role="line"
+ id="tspan4936-8-5-9"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">load</tspan><tspan
+ y="364.51511"
+ x="437.06458"
+ sodipodi:role="line"
+ id="tspan5466-8-8"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">quick-</tspan><tspan
+ y="372.19513"
+ x="437.06458"
+ sodipodi:role="line"
+ id="tspan6184"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mark <tspan
+ style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan3727">(8)</tspan></tspan></text>
+ <text
+ id="text9514-60-7-7-0"
+ y="315.02179"
+ x="436.65286"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="315.02179"
+ x="436.65286"
+ sodipodi:role="line"
+ id="tspan9524-92-1-3-7"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="322.70178"
+ x="436.65286"
+ sodipodi:role="line"
+ id="tspan4936-8-5-9-8"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">load</tspan><tspan
+ y="330.38177"
+ x="436.65286"
+ sodipodi:role="line"
+ id="tspan5466-8-8-0"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">quickm.</tspan><tspan
+ y="338.0618"
+ x="436.65286"
+ sodipodi:role="line"
+ id="tspan6186"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">(tab)</tspan><tspan
+ y="345.74179"
+ x="436.65286"
+ sodipodi:role="line"
+ id="tspan5689"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695"
+ width="344"
+ height="173.33333"
+ x="18.666666"
+ y="437.55823"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5697"
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan5705">(1)</flowSpan> copying/yanking:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5701">yy - copy/yank URL</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5703">yY - copy URL to selection</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5707">yt - copy title to clipboard</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5709">yT - copy title to selection</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5711"> </flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-0"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ transform="translate(0,-10)"><flowRegion
+ id="flowRegion5693-7"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-0"
+ width="344"
+ height="173.33333"
+ x="19.42783"
+ y="520.07886"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5697-9"
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan5705-5">(2)</flowSpan> pasting:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5701-9">pp - open URL from clipboard</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5703-8">pP - open URL from selection</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5707-0">Pp - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan6101">pp</flowSpan>, in new tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5709-3">PP - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan6103">pP</flowSpan>, in new tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5763">wp - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan6105">pp</flowSpan>, in new window</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5765">wP - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan6107">pP</flowSpan>, in new window</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5711-1"> </flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-0-9"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-7-0"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-0-5"
+ width="344"
+ height="173.33333"
+ x="201.3311"
+ y="437.55823"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"
+ id="flowPara5701-9-6"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan5705-5-8">(3)</flowSpan> navigation:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5829">[[ - click &quot;previous&quot;-link on page</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5703-8-2">]] - click &quot;next&quot;-link on page  </flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5707-0-7">{{ - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan6111">[[</flowSpan>, in new tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5709-3-1">}} - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan6109">]]</flowSpan>, in new tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5835">&lt;Ctrl-A&gt; - increment no. in URL</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5837">&lt;Ctrl-X&gt; - decrement no. in URL</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5841"> </flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5839" /><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5711-1-8" /></flowRoot> <text
+ id="text9514-8-9-0"
+ y="215.27605"
+ x="867.20831"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="215.27605"
+ x="867.20831"
+ sodipodi:role="line"
+ id="tspan4936-1-1-9"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(3)</tspan></text>
+ <text
+ id="text9514-8-9-0-3"
+ y="184.34271"
+ x="867.20831"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="184.34271"
+ x="867.20831"
+ sodipodi:role="line"
+ id="tspan4936-1-1-9-7"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(3)</tspan></text>
+ <text
+ id="text9514-8-9-0-2"
+ y="215.27605"
+ x="947.20831"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="215.27605"
+ x="947.20831"
+ sodipodi:role="line"
+ id="tspan4936-1-1-9-5"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(3)</tspan></text>
+ <text
+ id="text9514-8-9-0-6"
+ y="184.34271"
+ x="947.20831"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="184.34271"
+ x="947.20831"
+ sodipodi:role="line"
+ id="tspan4936-1-1-9-2"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(3)</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-4"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-9"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-9"
+ width="344"
+ height="173.33333"
+ x="199.82608"
+ y="546.74548"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5697-3"
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan5705-0">(4)</flowSpan> scrolling:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5701-8">&lt;Ctrl-F&gt; - page down</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5707-9">&lt;Ctrl-B&gt; - page up</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5962">&lt;Ctrl-D&gt; - half page down</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5711-7">&lt;Ctrl-U&gt; - half page up</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-4-9"
+ style="font-style:normal;font-weight:bold;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-9-1"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-9-8"
+ width="344"
+ height="173.33333"
+ x="403.53775"
+ y="437.55823"
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"
+ id="flowPara4171">in prompt mode:</flowPara><flowPara
+ style="font-weight:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4175">Enter - accept prompt</flowPara><flowPara
+ style="font-weight:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4177">y - answer yes to prompt</flowPara><flowPara
+ style="font-weight:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4179">n - answer no to prompt</flowPara><flowPara
+ style="font-weight:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara6016"> </flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-0-9-9"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;-inkscape-font-specification:Sans;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-7-0-2"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-0-5-6"
+ width="344"
+ height="173.33333"
+ x="403.54816"
+ y="496.42233"
+ style="font-style:normal;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ style="font-style:normal;font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"
+ id="flowPara5701-9-6-8"><flowSpan
+ style="font-style:normal;font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan5705-5-8-3">(6)</flowSpan> opening:</flowPara><flowPara
+ style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5829-1">go - edit &amp; open current URL</flowPara><flowPara
+ style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5703-8-2-8">gO - like  <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan6132">go</flowSpan>, in new tab</flowPara><flowPara
+ style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3581">xO - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan6134">go</flowSpan>, in bg. tab</flowPara><flowPara
+ style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5709-3-1-6">xo - open in background tab</flowPara><flowPara
+ style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5841-1">wo - open in new window</flowPara><flowPara
+ style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5839-8"> </flowPara><flowPara
+ style="font-style:normal;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:Sans;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5711-1-8-7" /></flowRoot> <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="425.7652"
+ y="272.26849"
+ id="text10547-23-6-1"><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="272.26849"
+ id="tspan10560-1-3-8"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="280.1514"
+ id="tspan10574-1-0-7"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">gg: <tspan
+ style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan3812">(10)</tspan></tspan><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="287.83142"
+ id="tspan6176"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">scroll</tspan><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="295.51141"
+ id="tspan5633-2"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">to top</tspan><tspan
+ sodipodi:role="line"
+ x="425.7652"
+ y="303.19141"
+ id="tspan10562-12-5-9"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="26.836851"
+ y="44.101078"
+ id="text10547-2-5"><tspan
+ sodipodi:role="line"
+ x="26.836851"
+ y="44.101078"
+ id="tspan10560-6-4"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="26.836851"
+ y="51.983994"
+ id="tspan5970-5"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">normal</tspan><tspan
+ sodipodi:role="line"
+ x="26.836851"
+ y="59.663994"
+ id="tspan6219"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mode</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-4-9-3"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-9-1-7"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-9-8-7"
+ width="344"
+ height="173.33333"
+ x="404.05209"
+ y="582.96967"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5697-3-7-6"
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan5705-0-4-7">(7)</flowSpan> back/forward:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara5701-8-5-8"><flowSpan
+ style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672"
+ id="flowSpan5463">th</flowSpan> - back (in new tab)</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara6016-5"><flowSpan
+ style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672"
+ id="flowSpan5465">wh</flowSpan> - back (in new window)</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3628"><flowSpan
+ style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672"
+ id="flowSpan5467">tl</flowSpan> - forward (in new tab)</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3630"><flowSpan
+ style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672"
+ id="flowSpan5469">wl</flowSpan> - forward (in new window)</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3626"> </flowPara></flowRoot> <text
+ id="text10564-6-7-8-3-5"
+ y="270.24557"
+ x="800"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10570-9-4-6-2-6"
+ y="270.24557"
+ x="800"
+ sodipodi:role="line"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="278.88559"
+ x="800"
+ sodipodi:role="line"
+ id="tspan4975-2-8-1-6"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="286.76849"
+ x="800"
+ sodipodi:role="line"
+ id="tspan4977-3-1-5-7"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">ext.</tspan><tspan
+ y="294.44849"
+ x="800"
+ sodipodi:role="line"
+ id="tspan5435-7"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">hints <tspan
+ style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan3662">(9)</tspan></tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-4-9-3-6"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-9-1-7-3"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-9-8-7-7"
+ width="344"
+ height="173.33333"
+ x="579.90106"
+ y="437.54779"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5697-3-7-6-8"
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672">(8)</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3626-7">prefix with w - in new window</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3725"> </flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-4-9-3-1"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-9-1-7-1"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-9-8-7-5"
+ width="344"
+ height="173.33333"
+ x="579.36774"
+ y="489.80301"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5697-3-7-6-1"
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan5705-0-4-7-6">(9)</flowSpan> extended hint mode:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3626-73">;b - open hint in background tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4051">;f - open hint in foreground tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3788">;h - hover over hint (mouse-over)</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3790">;i - hint images</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3792">;I - hint images in new tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara6096">;t - hint inputs</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3794">;o - put hinted URL in cmd. line</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3796">;O - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan3798">;o</flowSpan>, in new tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3800">;y - yank hinted URL to clipboard</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3802">;Y - yank hinted URL to selection</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3804">;r - rapid hinting</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3806">;R - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan3810">;r</flowSpan>, in new window</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3808">;d - download hinted URL</flowPara></flowRoot> <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-4-9-3-6-1"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-9-1-7-3-5"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-9-8-7-7-0"
+ width="165.23355"
+ height="256.78436"
+ x="772.63074"
+ y="437.55823"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5697-3-7-6-8-2"
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan3852">(10)</flowSpan> misc. commands:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3725-0">gt - switch tabs by name</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4052"><flowSpan
+ style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672"
+ id="flowSpan4054">gm/gl/gr</flowSpan> - move tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4056"> (to index/left/right)</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3858">gC - clone tab  </flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3860">gf - view page source</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3915">gu - navigate up in URL</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3917">gU - like <flowSpan
+ style="font-style:italic;font-family:sans-serif;-inkscape-font-specification:'Sans Italic';stroke-width:1.06666672"
+ id="flowSpan3923">gu</flowSpan>, in new tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3921">sf - save config</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3925">ss - set setting</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3927">sl - set temp. setting</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3929">sk - bind key</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3931">Ss - show settings</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3937">wi - open web inspector</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4167">gd - download page</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4169"><flowSpan
+ style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672"
+ id="flowSpan5438">ad</flowSpan> - cancel download</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4077">co - close other tabs</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4081">cd - clear downloads</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3933"> </flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3935" /><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4079" /></flowRoot> <text
+ id="text9514-8-9-0-8"
+ y="217.88069"
+ x="201.04399"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="217.88069"
+ x="201.04399"
+ sodipodi:role="line"
+ id="tspan4936-1-1-9-59"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(10)</tspan></text>
+ <text
+ id="text9514-8-9-0-8-4"
+ y="257.17371"
+ x="208.27393"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="257.17371"
+ x="208.27393"
+ sodipodi:role="line"
+ id="tspan4936-1-1-9-59-8"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(10)</tspan></text>
+ <text
+ id="text9514-8-9-0-8-7"
+ y="291.00937"
+ x="208.27393"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="291.00937"
+ x="208.27393"
+ sodipodi:role="line"
+ id="tspan4936-1-1-9-59-5"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(10)</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-4-9-3-6-1-2"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-9-1-7-3-5-2"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-9-8-7-7-0-9"
+ width="350.20157"
+ height="196.89922"
+ x="915.77972"
+ y="437.55823"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ id="flowPara5697-3-7-6-8-2-0"
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan3852-6">(11)</flowSpan> modifier commands:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3933-6">&lt;Alt-num&gt; - select tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4128">&lt;Ctrl-Tab&gt; - select prev. tab</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4130">&lt;Ctrl-V&gt; - passthrough mode</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4132">&lt;Ctrl-Q&gt; - quit</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4136">&lt;Ctrl-H&gt; - home</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4138">&lt;Ctrl-S&gt; - stop loading</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4140">&lt;Ctrl-Alt-P&gt; - print</flowPara><flowPara
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"
+ id="flowPara4142">in insert mode:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4144">&lt;Ctrl-E&gt; - open editor</flowPara><flowPara
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"
+ id="flowPara4146">in command mode:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara4148">&lt;Ctrl-P&gt; - prev. history item</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3935-9">&lt;Ctrl-N&gt; - next history item</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara6189">&lt;Ctrl-D&gt; - delete current item</flowPara></flowRoot> <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3764-9"
+ width="64"
+ height="49.059277"
+ x="179.54729"
+ y="386.13333"
+ ry="3.6638854" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3764-9-3"
+ width="64"
+ height="49.059277"
+ x="51.100777"
+ y="386.13333"
+ ry="3.6638854" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3764-9-1"
+ width="64"
+ height="49.059277"
+ x="654.73492"
+ y="386.13333"
+ ry="3.6638854" />
+ <rect
+ style="font-size:18px;fill:#eeeeec;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ id="rect3764-9-7"
+ width="64"
+ height="49.059277"
+ x="779.16278"
+ y="386.13333"
+ ry="3.6638854" />
+ <g
+ id="g4049"
+ transform="translate(1.4643921,-2.0969564)">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="66.420761"
+ y="411.5079"
+ id="text7358-8"><tspan
+ style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7360-1"
+ x="66.420761"
+ y="411.5079">Ctrl</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="71.803055"
+ y="426.94736"
+ id="text9514-8-9-0-8-4-0"><tspan
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan4936-1-1-9-59-8-3"
+ sodipodi:role="line"
+ x="71.803055"
+ y="426.94736">(11)</tspan></text>
+ </g>
+ <g
+ id="g4055"
+ transform="translate(1.7364258,-12.763623)">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="198.77023"
+ y="422.17459"
+ id="text7358-8-3-8-1"><tspan
+ style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7360-1-7-0-2"
+ x="198.77023"
+ y="422.17459">Alt</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="199.97752"
+ y="437.61404"
+ id="text9514-8-9-0-8-4-0-8"><tspan
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan4936-1-1-9-59-8-3-8"
+ sodipodi:role="line"
+ x="199.97752"
+ y="437.61404">(11)</tspan></text>
+ </g>
+ <g
+ id="g4065"
+ transform="translate(6.0870443,-12.763623)">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="669.60724"
+ y="422.17459"
+ id="text7358-8-3-8"><tspan
+ style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7360-1-7-0"
+ x="669.60724"
+ y="422.17459">Alt</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="670.81451"
+ y="437.61404"
+ id="text9514-8-9-0-8-4-0-7"><tspan
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan4936-1-1-9-59-8-3-82"
+ sodipodi:role="line"
+ x="670.81451"
+ y="437.61404">(11)</tspan></text>
+ </g>
+ <g
+ id="g4071"
+ transform="translate(1.0914714,-12.763623)">
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0%;font-family:'DejaVu Sans Mono';fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="794.85565"
+ y="422.17459"
+ id="text7358-8-3"><tspan
+ style="font-size:12.80000019px;line-height:1.25;font-family:'DejaVu Sans Mono';stroke-width:1.06666672"
+ sodipodi:role="line"
+ id="tspan7360-1-7"
+ x="794.85565"
+ y="422.17459">Ctrl</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="800.23798"
+ y="437.61404"
+ id="text9514-8-9-0-8-4-0-3"><tspan
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan4936-1-1-9-59-8-3-4"
+ sodipodi:role="line"
+ x="800.23798"
+ y="437.61404">(11)</tspan></text>
+ </g>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="416.72626"
+ y="171.09129"
+ id="text7245-1"><tspan
+ sodipodi:role="line"
+ x="416.72626"
+ y="171.09129"
+ id="tspan7366-3"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="416.72626"
+ y="178.97421"
+ id="tspan7249-4"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">select</tspan><tspan
+ sodipodi:role="line"
+ x="416.72626"
+ y="186.65421"
+ id="tspan5293"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">tab</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#ff0000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="292.49472"
+ y="366.05417"
+ id="text10547-23-6-7"><tspan
+ sodipodi:role="line"
+ x="292.49472"
+ y="366.05417"
+ id="tspan4052"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">(10)</tspan></text>
+ <text
+ id="text10564-6-7-8-0"
+ y="314.29889"
+ x="814.39789"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10570-9-4-6-1"
+ y="314.29889"
+ x="814.39789"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="321.97888"
+ x="814.39789"
+ sodipodi:role="line"
+ id="tspan4975-2-8-7"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="329.65887"
+ x="814.39789"
+ sodipodi:role="line"
+ id="tspan4977-3-1-2"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">search</tspan><tspan
+ y="337.3389"
+ x="814.39789"
+ sodipodi:role="line"
+ id="tspan5364"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">backw.</tspan></text>
+ <text
+ id="text9514-8-9-9"
+ y="171.64418"
+ x="788.19946"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="171.64418"
+ x="788.19946"
+ sodipodi:role="line"
+ id="tspan9524-5-1-5"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="179.5271"
+ x="788.19946"
+ sodipodi:role="line"
+ id="tspan6096-4"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">paste</tspan><tspan
+ y="187.20709"
+ x="788.19946"
+ sodipodi:role="line"
+ id="tspan5366-3"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(2)</tspan></text>
+ <text
+ id="text9514-8-0"
+ y="171.09129"
+ x="490.50494"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="171.09129"
+ x="490.50494"
+ sodipodi:role="line"
+ id="tspan9524-5-8"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ y="178.97421"
+ x="490.50494"
+ sodipodi:role="line"
+ id="tspan4938-4-2"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">yank/</tspan><tspan
+ y="186.65421"
+ x="490.50494"
+ sodipodi:role="line"
+ id="tspan4936-1-1"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">copy <tspan
+ style="font-size:8.53333378px;fill:#ff0000;stroke-width:1.06666672"
+ id="tspan5713-7">(1)</tspan></tspan></text>
+ <text
+ id="text9514-8-9-0-8-7-7"
+ y="291.00937"
+ x="133.7375"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="291.00937"
+ x="133.7375"
+ sodipodi:role="line"
+ id="tspan4936-1-1-9-59-5-6"
+ style="font-size:8.53333378px;line-height:0.89999998;fill:#ff0000;stroke-width:1.06666672">(10)</tspan></text>
+ <flowRoot
+ xml:space="preserve"
+ id="flowRoot5691-4-9-3-6-6"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ transform="translate(0,10)"><flowRegion
+ id="flowRegion5693-9-1-7-3-8"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-9-8-7-7-6"
+ width="344"
+ height="173.33333"
+ x="913.12598"
+ y="622.15717"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara3626-7-0"><flowSpan
+ id="flowSpan5520"
+ style="font-weight:bold;font-size:10.66666698px;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#0000ff;stroke-width:1.06666672">blue keys </flowSpan><flowSpan
+ style="font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672"
+ id="flowSpan5528">can be</flowSpan></flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#0000ff;stroke-width:1.06666672"
+ id="flowPara3725-9">prefixed by a count</flowPara></flowRoot> <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="339.1572"
+ y="166.24342"
+ id="text7245-1-7"><tspan
+ sodipodi:role="line"
+ x="339.1572"
+ y="166.24342"
+ id="tspan7366-3-3"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="339.1572"
+ y="174.12634"
+ id="tspan5293-5"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">reload </tspan><tspan
+ sodipodi:role="line"
+ x="339.1572"
+ y="181.80634"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan3716">(bypass </tspan><tspan
+ sodipodi:role="line"
+ x="339.1572"
+ y="189.48634"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan3719">cache)</tspan></text>
+ <text
+ id="text9514-60-7-7-0-8"
+ y="360.58533"
+ x="365.25357"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="360.58533"
+ x="365.25357"
+ sodipodi:role="line"
+ id="tspan5689-6"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">visual</tspan><tspan
+ y="368.26532"
+ x="365.25357"
+ sodipodi:role="line"
+ id="tspan4112"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mode</tspan></text>
+ <text
+ id="text10564-5"
+ y="274.2934"
+ x="873.4303"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="274.2934"
+ x="873.4303"
+ sodipodi:role="line"
+ id="tspan10566-6"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ id="tspan10570-91"
+ y="282.1763"
+ x="873.4303"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">jump to</tspan><tspan
+ y="289.85632"
+ x="873.4303"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6066">scroll</tspan><tspan
+ y="297.53632"
+ x="873.4303"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6068">mark</tspan></text>
+ <text
+ id="text10564-2"
+ y="362.50635"
+ x="731.82947"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ id="tspan10568-0"
+ y="362.50635"
+ x="731.82947"
+ sodipodi:role="line"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672">repeat</tspan><tspan
+ id="tspan10570-93"
+ y="370.38925"
+ x="731.82947"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">cmd</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="183.06667"
+ y="97.639633"
+ id="text7245-1-6"><tspan
+ sodipodi:role="line"
+ x="183.06667"
+ y="97.639633"
+ id="tspan7366-3-0"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="183.06667"
+ y="105.52255"
+ id="tspan7249-4-6"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">run</tspan><tspan
+ sodipodi:role="line"
+ x="183.06667"
+ y="113.20255"
+ id="tspan5293-2"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">macro</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="117.44301"
+ y="203.05061"
+ id="text7245-1-61"><tspan
+ sodipodi:role="line"
+ x="117.44301"
+ y="203.05061"
+ id="tspan7366-3-8"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="117.44301"
+ y="210.93353"
+ id="tspan5293-9"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">record</tspan><tspan
+ sodipodi:role="line"
+ x="117.44301"
+ y="218.61353"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6136">macro</tspan></text>
+ <text
+ id="text10564-5-2"
+ y="125.17836"
+ x="37.344757"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="125.17836"
+ x="37.344757"
+ sodipodi:role="line"
+ id="tspan10566-6-0"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ id="tspan10570-91-2"
+ y="133.06128"
+ x="37.344757"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">set</tspan><tspan
+ y="140.74127"
+ x="37.344757"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6066-3">scroll</tspan><tspan
+ y="148.42128"
+ x="37.344757"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6068-7">mark</tspan></text>
+ <text
+ id="text9514-60-8-5"
+ y="323.89648"
+ x="590.26257"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="323.89648"
+ x="590.26257"
+ sodipodi:role="line"
+ id="tspan5524-9"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">save</tspan><tspan
+ y="331.57648"
+ x="590.26257"
+ sodipodi:role="line"
+ id="tspan5530-2"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">book-</tspan><tspan
+ y="339.25647"
+ x="590.26257"
+ sodipodi:role="line"
+ id="tspan5532-2"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">mark</tspan></text>
+ <text
+ id="text10564-5-2-8"
+ y="200.40416"
+ x="21.280243"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ xml:space="preserve"><tspan
+ y="200.40416"
+ x="21.280243"
+ sodipodi:role="line"
+ id="tspan10566-6-0-9"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ id="tspan10570-91-2-7"
+ y="208.28708"
+ x="21.280243"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">cycle</tspan><tspan
+ y="215.96707"
+ x="21.280243"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6068-7-6">completion</tspan><tspan
+ y="223.64708"
+ x="21.280243"
+ sodipodi:role="line"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6220">items</tspan></text>
+ <text
+ xml:space="preserve"
+ style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:12.80000019px;line-height:0%;font-family:TlwgTypewriter;text-align:start;writing-mode:lr-tb;text-anchor:start;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"
+ x="417.29486"
+ y="205.18887"
+ id="text7245-1-3"><tspan
+ sodipodi:role="line"
+ x="417.29486"
+ y="205.18887"
+ id="tspan7366-3-6"
+ style="font-size:9.60000038px;line-height:0.89999998;stroke-width:1.06666672"> </tspan><tspan
+ sodipodi:role="line"
+ x="417.29486"
+ y="213.07179"
+ id="tspan5293-53"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672">toggle</tspan><tspan
+ sodipodi:role="line"
+ x="417.29486"
+ y="220.75179"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672;fill:#ff0000"
+ id="tspan6091">(12)</tspan><tspan
+ sodipodi:role="line"
+ x="417.29486"
+ y="225.70012"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6087" /><tspan
+ sodipodi:role="line"
+ x="417.29486"
+ y="225.70012"
+ style="font-size:8.53333378px;line-height:0.89999998;stroke-width:1.06666672"
+ id="tspan6089" /></text>
+ <flowRoot
+ transform="translate(-1.2953814,90.2721)"
+ xml:space="preserve"
+ id="flowRoot5691-0-5"
+ style="font-style:normal;font-weight:normal;font-size:12.80000019px;line-height:0.01%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.06666672"><flowRegion
+ id="flowRegion5693-7-6"
+ style="font-family:sans-serif;stroke-width:1.06666672"><rect
+ id="rect5695-0-2"
+ width="344"
+ height="173.33333"
+ x="19.42783"
+ y="520.07886"
+ style="font-family:sans-serif;fill:#000000;stroke-width:1.13777781" /></flowRegion><flowPara
+ style="font-weight:bold;font-size:10.66666698px;line-height:1.25;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#000000;stroke-width:1.06666672"
+ id="flowPara5701-9-2"><flowSpan
+ style="font-weight:bold;font-family:sans-serif;-inkscape-font-specification:'Sans Bold';fill:#ff0000;stroke-width:1.06666672"
+ id="flowSpan5705-5-1">(12)</flowSpan> toggling settings:</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara6196">tsh - toggle scripts for the current host (temporarily)</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara6200">tSh - like <flowSpan
+ style="font-style:italic"
+ id="flowSpan6202">tsh</flowSpan>, but permanently</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara6206">tsH/tsu - like <flowSpan
+ style="font-style:italic"
+ id="flowSpan6210">tsh</flowSpan>, but including subdomains / with exact URL</flowPara><flowPara
+ style="font-size:10.66666698px;line-height:1.25;font-family:sans-serif;fill:#000000;stroke-width:1.06666672"
+ id="flowPara6208">tph - toggle plugins</flowPara></flowRoot> </g>
+</svg>
diff --git a/.config/qutebrowser/misc/qutebrowser.appdata.xml b/.config/qutebrowser/misc/qutebrowser.appdata.xml
new file mode 100644
index 0000000..bdab55f
--- /dev/null
+++ b/.config/qutebrowser/misc/qutebrowser.appdata.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Copyright 2017 suve <veg@svgames.pl> -->
+<component type="desktop">
+ <id>org.qutebrowser.qutebrowser</id>
+ <metadata_license>CC-BY-SA-3.0</metadata_license>
+ <project_license>GPL-3.0</project_license>
+ <name>qutebrowser</name>
+ <summary>A keyboard-driven web browser</summary>
+ <description>
+ <p>
+ qutebrowser is a keyboard-focused browser with a minimal GUI.
+ It was inspired by other browsers/addons like dwb and Vimperator/Pentadactyl,
+ and is based on Python and PyQt5.
+ </p>
+ </description>
+ <categories>
+ <category>Network</category>
+ <category>WebBrowser</category>
+ </categories>
+ <provides>
+ <binary>qutebrowser</binary>
+ </provides>
+ <launchable type="desktop-id">qutebrowser.desktop</launchable>
+ <screenshots>
+ <screenshot type="default">
+ <image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/main.png</image>
+ </screenshot>
+ <screenshot>
+ <image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/downloads.png</image>
+ </screenshot>
+ <screenshot>
+ <image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/completion.png</image>
+ </screenshot>
+ <screenshot>
+ <image>https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/hints.png</image>
+ </screenshot>
+ </screenshots>
+ <url type="homepage">https://www.qutebrowser.org</url>
+ <url type="faq">https://qutebrowser.org/doc/faq.html</url>
+ <url type="help">https://qutebrowser.org/doc/help/</url>
+ <url type="bugtracker">https://github.com/qutebrowser/qutebrowser/issues/</url>
+ <url type="donation">https://github.com/qutebrowser/qutebrowser#donating</url>
+ <releases>
+ <release version="1.3.0" date="2018-05-04"/>
+ <release version="1.2.1" date="2018-03-14"/>
+ <release version="1.2.0" date="2018-03-09"/>
+ </releases>
+</component>
diff --git a/.config/qutebrowser/misc/qutebrowser.desktop b/.config/qutebrowser/misc/qutebrowser.desktop
new file mode 100644
index 0000000..5243b0c
--- /dev/null
+++ b/.config/qutebrowser/misc/qutebrowser.desktop
@@ -0,0 +1,12 @@
+[Desktop Entry]
+Name=qutebrowser
+GenericName=Web Browser
+Comment=A keyboard-driven, vim-like browser based on PyQt5
+Icon=qutebrowser
+Type=Application
+Categories=Network;WebBrowser;
+Exec=qutebrowser %u
+Terminal=false
+StartupNotify=false
+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;
+Keywords=Browser
diff --git a/.config/qutebrowser/misc/qutebrowser.nsi b/.config/qutebrowser/misc/qutebrowser.nsi
new file mode 100644
index 0000000..76f459a
--- /dev/null
+++ b/.config/qutebrowser/misc/qutebrowser.nsi
@@ -0,0 +1,80 @@
+Name "qutebrowser"
+
+Unicode true
+RequestExecutionLevel admin
+SetCompressor /solid lzma
+
+!ifdef X64
+ OutFile "..\dist\qutebrowser-${VERSION}-amd64.exe"
+ InstallDir "$ProgramFiles64\qutebrowser"
+!else
+ OutFile "..\dist\qutebrowser-${VERSION}-win32.exe"
+ InstallDir "$ProgramFiles\qutebrowser"
+!endif
+
+;Default installation folder
+
+!include "MUI2.nsh"
+;!include "MultiUser.nsh"
+
+!define MUI_ABORTWARNING
+;!define MULTIUSER_MUI
+;!define MULTIUSER_INSTALLMODE_COMMANDLINE
+!define MUI_ICON "../icons/qutebrowser.ico"
+!define MUI_UNICON "../icons/qutebrowser.ico"
+
+!insertmacro MUI_PAGE_LICENSE "..\LICENSE"
+!insertmacro MUI_PAGE_DIRECTORY
+!insertmacro MUI_PAGE_INSTFILES
+!insertmacro MUI_UNPAGE_CONFIRM
+!insertmacro MUI_UNPAGE_INSTFILES
+
+!insertmacro MUI_LANGUAGE "English"
+
+; depends on admin status
+;SetShellVarContext current
+
+
+Section "Install"
+
+ ; Uninstall old versions
+ ExecWait 'MsiExec.exe /quiet /qn /norestart /X{633F41F9-FE9B-42D1-9CC4-718CBD01EE11}'
+ ExecWait 'MsiExec.exe /quiet /qn /norestart /X{9331D947-AC86-4542-A755-A833429C6E69}'
+ IfFileExists "$INSTDIR\uninst.exe" 0 +2
+ ExecWait "$INSTDIR\uninst.exe /S _?=$INSTDIR"
+ CreateDirectory "$INSTDIR"
+
+ SetOutPath "$INSTDIR"
+
+ !ifdef X64
+ file /r "..\dist\qutebrowser-${VERSION}-x64\*.*"
+ !else
+ file /r "..\dist\qutebrowser-${VERSION}-x86\*.*"
+ !endif
+
+ SetShellVarContext all
+ CreateShortCut "$SMPROGRAMS\qutebrowser.lnk" "$INSTDIR\qutebrowser.exe"
+
+ ;Create uninstaller
+ WriteUninstaller "$INSTDIR\uninst.exe"
+
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "DisplayName" "qutebrowser"
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "UninstallString" '"$INSTDIR\uninst.exe"'
+ WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser" "QuietUninstallString" '"$INSTDIR\uninst.exe" /S'
+
+SectionEnd
+
+;--------------------------------
+;Uninstaller Section
+
+Section "Uninstall"
+
+ SetShellVarContext all
+ Delete "$SMPROGRAMS\qutebrowser.lnk"
+
+ RMDir /r "$INSTDIR\*.*"
+ RMDir "$INSTDIR"
+
+ DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\qutebrowser"
+
+SectionEnd
diff --git a/.config/qutebrowser/misc/qutebrowser.rcc b/.config/qutebrowser/misc/qutebrowser.rcc
new file mode 100644
index 0000000..2f73b37
--- /dev/null
+++ b/.config/qutebrowser/misc/qutebrowser.rcc
@@ -0,0 +1,13 @@
+<!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/.config/qutebrowser/misc/qutebrowser.spec b/.config/qutebrowser/misc/qutebrowser.spec
new file mode 100644
index 0000000..c886fb0
--- /dev/null
+++ b/.config/qutebrowser/misc/qutebrowser.spec
@@ -0,0 +1,76 @@
+# -*- mode: python -*-
+
+import sys
+import os
+
+sys.path.insert(0, os.getcwd())
+from scripts import setupcommon
+
+block_cipher = None
+
+
+def get_data_files():
+ data_files = [
+ ('../qutebrowser/html', 'html'),
+ ('../qutebrowser/img', 'img'),
+ ('../qutebrowser/javascript', 'javascript'),
+ ('../qutebrowser/html/doc', 'html/doc'),
+ ('../qutebrowser/git-commit-id', '.'),
+ ('../qutebrowser/config/configdata.yml', 'config'),
+ ]
+
+ # if os.path.exists(os.path.join('qutebrowser', '3rdparty', 'pdfjs')):
+ # data_files.append(('../qutebrowser/3rdparty/pdfjs', '3rdparty/pdfjs'))
+ # else:
+ # print("Warning: excluding pdfjs as it's not present!")
+
+ return data_files
+
+
+setupcommon.write_git_file()
+
+
+if os.name == 'nt':
+ icon = 'icons/qutebrowser.ico'
+elif sys.platform == 'darwin':
+ icon = 'icons/qutebrowser.icns'
+else:
+ icon = None
+
+
+a = Analysis(['../qutebrowser/__main__.py'],
+ pathex=['misc'],
+ binaries=None,
+ datas=get_data_files(),
+ hiddenimports=['PyQt5.QtOpenGL', 'PyQt5._QOpenGLFunctions_2_0'],
+ hookspath=[],
+ runtime_hooks=[],
+ excludes=['tkinter'],
+ win_no_prefer_redirects=False,
+ win_private_assemblies=False,
+ cipher=block_cipher)
+pyz = PYZ(a.pure, a.zipped_data,
+ cipher=block_cipher)
+exe = EXE(pyz,
+ a.scripts,
+ exclude_binaries=True,
+ name='qutebrowser',
+ icon=icon,
+ debug=False,
+ strip=False,
+ upx=False,
+ console=False,
+ version='misc/file_version_info.txt')
+coll = COLLECT(exe,
+ a.binaries,
+ a.zipfiles,
+ a.datas,
+ strip=False,
+ upx=False,
+ name='qutebrowser')
+
+app = BUNDLE(coll,
+ name='qutebrowser.app',
+ icon=icon,
+ # https://github.com/pyinstaller/pyinstaller/blob/b78bfe530cdc2904f65ce098bdf2de08c9037abb/PyInstaller/hooks/hook-PyQt5.QtWebEngineWidgets.py#L24
+ bundle_identifier='org.qt-project.Qt.QtWebEngineCore')
diff --git a/.config/qutebrowser/misc/requirements/README.md b/.config/qutebrowser/misc/requirements/README.md
new file mode 100644
index 0000000..6ae9862
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/README.md
@@ -0,0 +1,20 @@
+This directory contains various `requirements` files which are used by `tox` to
+have reproducible tests with pinned versions.
+
+The files are generated based on unpinned requirements in `*.txt-raw` files.
+
+Those files can also contain some special commands:
+
+- Add an additional comment to a line: `#@ comment: <package> <comment here>`
+- Filter a line for requirements.io: `#@ filter: <package> <filter>`
+- Don't include a package in the output: `#@ ignore: <package>` (or multiple packages)
+- Replace a part of a frozen package specification with another: `#@ replace <regex> <replacement>`
+
+Some examples:
+
+```
+#@ comment: mypkg blah blub
+#@ filter: mypkg != 1.0.0
+#@ ignore: mypkg, otherpkg
+#@ replace: foo bar
+```
diff --git a/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt b/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt
new file mode 100644
index 0000000..c11a3f7
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt
@@ -0,0 +1,3 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+check-manifest==0.37
diff --git a/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt-raw b/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt-raw
new file mode 100644
index 0000000..dcc0efe
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-check-manifest.txt-raw
@@ -0,0 +1 @@
+check-manifest
diff --git a/.config/qutebrowser/misc/requirements/requirements-codecov.txt b/.config/qutebrowser/misc/requirements/requirements-codecov.txt
new file mode 100644
index 0000000..9fed7b3
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-codecov.txt
@@ -0,0 +1,9 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+certifi==2018.4.16
+chardet==3.0.4
+codecov==2.0.15
+coverage==4.5.1
+idna==2.7
+requests==2.19.1
+urllib3==1.23
diff --git a/.config/qutebrowser/misc/requirements/requirements-codecov.txt-raw b/.config/qutebrowser/misc/requirements/requirements-codecov.txt-raw
new file mode 100644
index 0000000..15f1c72
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-codecov.txt-raw
@@ -0,0 +1 @@
+codecov
diff --git a/.config/qutebrowser/misc/requirements/requirements-flake8.txt b/.config/qutebrowser/misc/requirements/requirements-flake8.txt
new file mode 100644
index 0000000..b4f0045
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-flake8.txt
@@ -0,0 +1,27 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+attrs==18.1.0
+flake8==3.5.0
+flake8-bugbear==18.2.0
+flake8-builtins==1.4.1 # rq.filter: != 1.4.0
+flake8-comprehensions==1.4.1
+flake8-copyright==0.2.0
+flake8-debugger==3.1.0
+flake8-deprecated==1.3
+flake8-docstrings==1.3.0
+flake8-future-import==0.4.5
+flake8-mock==0.3
+flake8-per-file-ignores==0.6
+flake8-polyfill==1.0.2
+flake8-string-format==0.2.3
+flake8-tidy-imports==1.1.0
+flake8-tuple==0.2.13
+mccabe==0.6.1
+pathmatch==0.2.1
+pep8-naming==0.7.0
+pycodestyle==2.3.1 # rq.filter: < 2.4.0
+pydocstyle==2.1.1
+pyflakes==2.0.0
+six==1.11.0
+snowballstemmer==1.2.1
+typing==3.6.4
diff --git a/.config/qutebrowser/misc/requirements/requirements-flake8.txt-raw b/.config/qutebrowser/misc/requirements/requirements-flake8.txt-raw
new file mode 100644
index 0000000..7ccbbce
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-flake8.txt-raw
@@ -0,0 +1,23 @@
+flake8
+flake8-bugbear
+flake8-builtins!=1.4.0
+flake8-comprehensions
+flake8-copyright
+flake8-debugger
+flake8-deprecated
+flake8-docstrings
+flake8-future-import
+flake8-mock
+flake8-per-file-ignores
+flake8-string-format
+flake8-tidy-imports
+flake8-tuple
+pep8-naming
+pydocstyle
+pyflakes
+
+# https://github.com/PyCQA/pycodestyle/issues/741
+#@ filter: pycodestyle < 2.4.0
+
+# https://github.com/gforcada/flake8-builtins/issues/36
+#@ filter: flake8-builtins != 1.4.0
diff --git a/.config/qutebrowser/misc/requirements/requirements-pip.txt b/.config/qutebrowser/misc/requirements/requirements-pip.txt
new file mode 100644
index 0000000..bf003fc
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pip.txt
@@ -0,0 +1,8 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+appdirs==1.4.3
+packaging==17.1
+pyparsing==2.2.0
+setuptools==40.0.0
+six==1.11.0
+wheel==0.31.1
diff --git a/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt b/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt
new file mode 100644
index 0000000..e916393
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt
@@ -0,0 +1,7 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+altgraph==0.15
+future==0.16.0
+macholib==1.9
+pefile==2017.11.5
+PyInstaller==3.3.1
diff --git a/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt-raw
new file mode 100644
index 0000000..c313980
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pyinstaller.txt-raw
@@ -0,0 +1 @@
+PyInstaller
diff --git a/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt b/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt
new file mode 100644
index 0000000..2df0736
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt
@@ -0,0 +1,19 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+-e git+https://github.com/PyCQA/astroid.git#egg=astroid
+certifi==2018.4.16
+chardet==3.0.4
+github3.py==1.1.0
+idna==2.7
+isort==4.3.4
+lazy-object-proxy==1.3.1
+mccabe==0.6.1
+-e git+https://github.com/PyCQA/pylint.git#egg=pylint
+python-dateutil==2.7.3
+./scripts/dev/pylint_checkers
+requests==2.19.1
+six==1.11.0
+typed-ast==1.1.0
+uritemplate==3.0.0
+urllib3==1.23
+wrapt==1.10.11
diff --git a/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt-raw
new file mode 100644
index 0000000..405b0ab
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pylint-master.txt-raw
@@ -0,0 +1,11 @@
+-e git+https://github.com/PyCQA/astroid.git#egg=astroid
+-e git+https://github.com/PyCQA/pylint.git#egg=pylint
+./scripts/dev/pylint_checkers
+requests
+github3.py
+
+# remove @commit-id for scm installs
+#@ replace: @.*# #
+
+# fix qute-pylint location
+#@ replace: qute-pylint==.* ./scripts/dev/pylint_checkers
diff --git a/.config/qutebrowser/misc/requirements/requirements-pylint.txt b/.config/qutebrowser/misc/requirements/requirements-pylint.txt
new file mode 100644
index 0000000..e78dfe2
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pylint.txt
@@ -0,0 +1,19 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+astroid==2.0.1
+certifi==2018.4.16
+chardet==3.0.4
+github3.py==1.1.0
+idna==2.7
+isort==4.3.4
+lazy-object-proxy==1.3.1
+mccabe==0.6.1
+pylint==2.0.1
+python-dateutil==2.7.3
+./scripts/dev/pylint_checkers
+requests==2.19.1
+six==1.11.0
+typed-ast==1.1.0
+uritemplate==3.0.0
+urllib3==1.23
+wrapt==1.10.11
diff --git a/.config/qutebrowser/misc/requirements/requirements-pylint.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pylint.txt-raw
new file mode 100644
index 0000000..37252ee
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pylint.txt-raw
@@ -0,0 +1,7 @@
+pylint
+./scripts/dev/pylint_checkers
+requests
+github3.py
+
+# fix qute-pylint location
+#@ replace: qute-pylint==.* ./scripts/dev/pylint_checkers
diff --git a/.config/qutebrowser/misc/requirements/requirements-pyqt.txt b/.config/qutebrowser/misc/requirements/requirements-pyqt.txt
new file mode 100644
index 0000000..2878a55
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pyqt.txt
@@ -0,0 +1,4 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+PyQt5==5.11.2
+PyQt5-sip==4.19.12
diff --git a/.config/qutebrowser/misc/requirements/requirements-pyqt.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pyqt.txt-raw
new file mode 100644
index 0000000..37a69c4
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pyqt.txt-raw
@@ -0,0 +1 @@
+PyQt5 \ No newline at end of file
diff --git a/.config/qutebrowser/misc/requirements/requirements-pyroma.txt b/.config/qutebrowser/misc/requirements/requirements-pyroma.txt
new file mode 100644
index 0000000..6afd097
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pyroma.txt
@@ -0,0 +1,4 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+docutils==0.14
+pyroma==2.3.1
diff --git a/.config/qutebrowser/misc/requirements/requirements-pyroma.txt-raw b/.config/qutebrowser/misc/requirements/requirements-pyroma.txt-raw
new file mode 100644
index 0000000..5ddfb65
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-pyroma.txt-raw
@@ -0,0 +1 @@
+pyroma
diff --git a/.config/qutebrowser/misc/requirements/requirements-qutebrowser.txt-raw b/.config/qutebrowser/misc/requirements/requirements-qutebrowser.txt-raw
new file mode 100644
index 0000000..c66c65b
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-qutebrowser.txt-raw
@@ -0,0 +1,7 @@
+Jinja2
+Pygments
+pyPEG2
+PyYAML
+colorama
+cssutils
+attrs
diff --git a/.config/qutebrowser/misc/requirements/requirements-tests-git.txt b/.config/qutebrowser/misc/requirements/requirements-tests-git.txt
new file mode 100644
index 0000000..ce00cd3
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-tests-git.txt
@@ -0,0 +1,38 @@
+bzr+lp:beautifulsoup
+git+https://github.com/cherrypy/cheroot.git
+hg+https://bitbucket.org/ned/coveragepy
+git+https://github.com/micheles/decorator.git
+git+https://github.com/pallets/flask.git
+git+https://github.com/miracle2k/python-glob2.git
+git+https://github.com/HypothesisWorks/hypothesis-python.git
+git+https://github.com/pallets/itsdangerous.git
+git+https://bitbucket.org/zzzeek/mako.git
+git+https://github.com/r1chardj0n3s/parse.git
+git+https://github.com/jenisys/parse_type.git
+hg+https://bitbucket.org/pytest-dev/py
+git+https://github.com/pytest-dev/pytest.git@features
+git+https://github.com/pytest-dev/pytest-bdd.git
+git+https://github.com/pytest-dev/pytest-cov.git
+git+https://github.com/pytest-dev/pytest-faulthandler.git
+git+https://github.com/pytest-dev/pytest-instafail.git
+git+https://github.com/pytest-dev/pytest-mock.git
+git+https://github.com/pytest-dev/pytest-qt.git
+git+https://github.com/pytest-dev/pytest-repeat.git
+git+https://github.com/pytest-dev/pytest-rerunfailures.git
+git+https://github.com/abusalimov/pytest-travis-fold.git
+git+https://github.com/The-Compiler/pytest-xvfb.git
+hg+https://bitbucket.org/gutworth/six
+hg+https://bitbucket.org/jendrikseipp/vulture
+git+https://github.com/pallets/werkzeug.git
+
+
+## qutebrowser dependencies
+
+git+https://github.com/tartley/colorama.git
+hg+https://bitbucket.org/cthedot/cssutils
+git+https://github.com/pallets/jinja.git
+git+https://github.com/pallets/markupsafe.git
+hg+http://bitbucket.org/birkenfeld/pygments-main
+hg+https://bitbucket.org/fdik/pypeg
+git+https://github.com/python-attrs/attrs.git
+git+https://github.com/yaml/pyyaml.git
diff --git a/.config/qutebrowser/misc/requirements/requirements-tests.txt b/.config/qutebrowser/misc/requirements/requirements-tests.txt
new file mode 100644
index 0000000..07b0378
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-tests.txt
@@ -0,0 +1,42 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+atomicwrites==1.1.5
+attrs==18.1.0
+backports.functools-lru-cache==1.5
+beautifulsoup4==4.6.0
+cheroot==6.3.3
+click==6.7
+# colorama==0.3.9
+coverage==4.5.1
+EasyProcess==0.2.3
+fields==5.0.0
+Flask==1.0.2
+glob2==0.6
+hunter==2.0.2
+hypothesis==3.66.6
+itsdangerous==0.24
+# Jinja2==2.10
+Mako==1.0.7
+# MarkupSafe==1.0
+more-itertools==4.2.0
+parse==1.8.4
+parse-type==0.4.2
+pluggy==0.6.0
+py==1.5.4
+py-cpuinfo==4.0.0
+pytest==3.6.3
+pytest-bdd==2.21.0
+pytest-benchmark==3.1.1
+pytest-cov==2.5.1
+pytest-faulthandler==1.5.0
+pytest-instafail==0.4.0
+pytest-mock==1.10.0
+pytest-qt==3.0.0
+pytest-repeat==0.5.0
+pytest-rerunfailures==4.1
+pytest-travis-fold==1.3.0
+pytest-xvfb==1.1.0
+PyVirtualDisplay==0.2.1
+six==1.11.0
+vulture==0.28
+Werkzeug==0.14.1
diff --git a/.config/qutebrowser/misc/requirements/requirements-tests.txt-raw b/.config/qutebrowser/misc/requirements/requirements-tests.txt-raw
new file mode 100644
index 0000000..1216899
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-tests.txt-raw
@@ -0,0 +1,21 @@
+beautifulsoup4
+cheroot
+coverage
+Flask
+hunter
+hypothesis
+pytest
+pytest-bdd
+pytest-benchmark
+pytest-cov
+pytest-faulthandler
+pytest-instafail
+pytest-mock
+pytest-qt
+pytest-repeat
+pytest-rerunfailures
+pytest-travis-fold
+pytest-xvfb
+vulture
+
+#@ ignore: Jinja2, MarkupSafe, colorama
diff --git a/.config/qutebrowser/misc/requirements/requirements-tox.txt b/.config/qutebrowser/misc/requirements/requirements-tox.txt
new file mode 100644
index 0000000..e8c660e
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-tox.txt
@@ -0,0 +1,9 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+packaging==17.1
+pluggy==0.6.0
+py==1.5.4
+pyparsing==2.2.0
+six==1.11.0
+tox==3.1.2
+virtualenv==16.0.0
diff --git a/.config/qutebrowser/misc/requirements/requirements-tox.txt-raw b/.config/qutebrowser/misc/requirements/requirements-tox.txt-raw
new file mode 100644
index 0000000..053148f
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-tox.txt-raw
@@ -0,0 +1 @@
+tox
diff --git a/.config/qutebrowser/misc/requirements/requirements-vulture.txt b/.config/qutebrowser/misc/requirements/requirements-vulture.txt
new file mode 100644
index 0000000..c8a26e8
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-vulture.txt
@@ -0,0 +1,3 @@
+# This file is automatically generated by scripts/dev/recompile_requirements.py
+
+vulture==0.28
diff --git a/.config/qutebrowser/misc/requirements/requirements-vulture.txt-raw b/.config/qutebrowser/misc/requirements/requirements-vulture.txt-raw
new file mode 100644
index 0000000..a10f860
--- /dev/null
+++ b/.config/qutebrowser/misc/requirements/requirements-vulture.txt-raw
@@ -0,0 +1 @@
+vulture
diff --git a/.config/qutebrowser/misc/userscripts/cast b/.config/qutebrowser/misc/userscripts/cast
new file mode 100755
index 0000000..f7b64df
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/cast
@@ -0,0 +1,156 @@
+#!/usr/bin/env bash
+#
+# Behaviour
+# Userscript for qutebrowser which casts the url passed in $1 to the default
+# ChromeCast device in the network using the program `castnow`
+#
+# Usage
+# You can launch the script from qutebrowser as follows:
+# spawn --userscript ${PATH_TO_FILE} {url}
+#
+# Then, you can control the chromecast by launching the simple command
+# `castnow` in a shell which will connect to the running castnow instance.
+#
+# For stopping the script, issue the command `pkill -f castnow` which would
+# then let the rest of the userscript execute for cleaning temporary file.
+#
+# Thanks
+# This userscript borrows Thorsten Wißmann's javascript code from his `mpv`
+# userscript.
+#
+# Dependencies
+# - castnow, https://github.com/xat/castnow
+#
+# Author
+# Simon Désaulniers <sim.desaulniers@gmail.com>
+
+if [ -z "$QUTE_FIFO" ] ; then
+ cat 1>&2 <<EOF
+Error: $0 can not be run as a standalone script.
+
+It is a qutebrowser userscript. In order to use it, call it using
+'spawn --userscript' as described in qute://help/userscripts.html
+EOF
+ exit 1
+fi
+
+msg() {
+ local cmd="$1"
+ shift
+ local msg="$*"
+ if [ -z "$QUTE_FIFO" ] ; then
+ echo "$cmd: $msg" >&2
+ else
+ echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
+ fi
+}
+
+js() {
+cat <<EOF
+
+ function descendantOfTagName(child, ancestorTagName) {
+ // tells whether child has some (proper) ancestor
+ // with the tag name ancestorTagName
+ while (child.parentNode != null) {
+ child = child.parentNode;
+ if (typeof child.tagName === 'undefined') break;
+ if (child.tagName.toUpperCase() == ancestorTagName.toUpperCase()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ var App = {};
+
+ var all_videos = [];
+ all_videos.push.apply(all_videos, document.getElementsByTagName("video"));
+ all_videos.push.apply(all_videos, document.getElementsByTagName("object"));
+ all_videos.push.apply(all_videos, document.getElementsByTagName("embed"));
+ App.backup_videos = Array();
+ App.all_replacements = Array();
+ for (i = 0; i < all_videos.length; i++) {
+ var video = all_videos[i];
+ if (descendantOfTagName(video, "object")) {
+ // skip tags that are contained in an object, because we hide
+ // the object anyway.
+ continue;
+ }
+ var replacement = document.createElement("div");
+ replacement.innerHTML = "
+ <p style=\\"margin-bottom: 0.5em\\">
+ The video is being cast on your ChromeCast device.
+ </p>
+ <p>
+ In order to restore this particular video
+ <a style=\\"font-weight: bold;
+ color: white;
+ background: transparent;
+ \\"
+ onClick=\\"restore_video(this, " + i + ");\\"
+ href=\\"javascript: restore_video(this, " + i + ")\\"
+ >click here</a>.
+ </p>
+ ";
+ replacement.style.position = "relative";
+ replacement.style.zIndex = "100003000000";
+ replacement.style.fontSize = "1rem";
+ replacement.style.textAlign = "center";
+ replacement.style.verticalAlign = "middle";
+ replacement.style.height = "100%";
+ replacement.style.background = "#101010";
+ replacement.style.color = "white";
+ replacement.style.border = "4px dashed #545454";
+ replacement.style.padding = "2em";
+ replacement.style.margin = "auto";
+ App.all_replacements[i] = replacement;
+ App.backup_videos[i] = video;
+ video.parentNode.replaceChild(replacement, video);
+ }
+
+ function restore_video(obj, index) {
+ obj = App.all_replacements[index];
+ video = App.backup_videos[index];
+ console.log(video);
+ obj.parentNode.replaceChild(video, obj);
+ }
+
+ /** force repainting the video, thanks to:
+ * http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/
+ */
+ var siteHeader = document.getElementById('header');
+ siteHeader.style.display='none';
+ siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough
+ siteHeader.style.display='block';
+
+EOF
+}
+
+printjs() {
+ js | sed 's,//.*$,,' | tr '\n' ' '
+}
+echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
+
+tmpdir=$(mktemp -d)
+file_to_cast=${tmpdir}/qutecast
+program_=$(command -v castnow)
+
+if [[ "${program_}" == "" ]]; then
+ msg error "castnow can't be found..."
+ exit 1
+fi
+
+# kill any running instance of castnow
+pkill -f "${program_}"
+
+# start youtube download in stream mode (-o -) into temporary file
+youtube-dl -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_} -
+
+# cleanup remaining background process and file on disk
+kill ${ytdl_pid}
+rm -rf "${tmpdir}"
diff --git a/.config/qutebrowser/misc/userscripts/dmenu_qutebrowser b/.config/qutebrowser/misc/userscripts/dmenu_qutebrowser
new file mode 100755
index 0000000..82e6d2f
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/dmenu_qutebrowser
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+
+# Copyright 2015 Zach-Button <zachrey.button@gmail.com>
+# Copyright 2015-2017 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 <http://www.gnu.org/licenses/>.
+
+# Pipes history, quickmarks, and URL into dmenu.
+#
+# If run from qutebrowser as a userscript, it runs :open on the URL
+# If not, it opens a new qutebrowser window at the URL
+#
+# Ideal for use with tabs_are_windows. Set a hotkey to launch this script, then:
+# :bind o spawn --userscript dmenu_qutebrowser
+#
+# Use the hotkey to open in new tab/window, press 'o' to open URL in current tab/window
+# You can simulate "go" by pressing "o<tab>", as the current URL is always first in the list
+#
+# I personally use "<Mod4>o" to launch this script. For me, my workflow is:
+# Default keys Keys with this script
+# O <Mod4>o
+# o o
+# go o<Tab>
+# gO gC, then o<Tab>
+# (This is unnecessarily long. I use this rarely, feel free to make this script accept parameters.)
+#
+
+[ -z "$QUTE_URL" ] && QUTE_URL='http://google.com'
+
+url=$(echo "$QUTE_URL" | cat - "$QUTE_CONFIG_DIR/quickmarks" "$QUTE_DATA_DIR/history" | dmenu -l 15 -p qutebrowser)
+url=$(echo "$url" | sed -E 's/[^ ]+ +//g' | grep -E "https?:" || echo "$url")
+
+[ -z "${url// }" ] && exit
+
+echo "open $url" >> "$QUTE_FIFO" || qutebrowser "$url"
diff --git a/.config/qutebrowser/misc/userscripts/format_json b/.config/qutebrowser/misc/userscripts/format_json
new file mode 100755
index 0000000..0d476b3
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/format_json
@@ -0,0 +1,42 @@
+#!/bin/sh
+set -euo pipefail
+#
+# Behavior:
+# Userscript for qutebrowser which will take the raw JSON text of the current
+# page, format it using `jq`, will add syntax highlighting using `pygments`,
+# and open the syntax highlighted pretty printed html in a new tab. If the file
+# is larger than 10MB then this script will only indent the json and will forego
+# syntax highlighting using pygments.
+#
+# In order to use this script, just start it using `spawn --userscript` from
+# qutebrowser. I recommend using an alias, e.g. put this in the
+# [alias]-section of qutebrowser.conf:
+#
+# json = spawn --userscript /path/to/json_format
+#
+# Note that the color style defaults to monokai, but a different pygments style
+# can be passed as the first parameter to the script. A full list of the pygments
+# styles can be found at: https://help.farbox.com/pygments.html
+#
+# Bryan Gilbert, 2017
+
+# do not run pygmentize on files larger than this amount of bytes
+MAX_SIZE_PRETTIFY=10485760 # 10 MB
+# default style to monokai if none is provided
+STYLE=${1:-monokai}
+
+TEMP_FILE="$(mktemp)"
+jq . "$QUTE_TEXT" >"$TEMP_FILE"
+
+# try GNU stat first and then OSX stat if the former fails
+FILE_SIZE=$(
+ stat --printf="%s" "$TEMP_FILE" 2>/dev/null ||
+ stat -f%z "$TEMP_FILE" 2>/dev/null
+)
+if [ "$FILE_SIZE" -lt "$MAX_SIZE_PRETTIFY" ]; then
+ pygmentize -l json -f html -O full,style="$STYLE" <"$TEMP_FILE" >"${TEMP_FILE}_"
+ mv -f "${TEMP_FILE}_" "$TEMP_FILE"
+fi
+
+# send the command to qutebrowser to open the new file containing the formatted json
+echo "open -t file://$TEMP_FILE" >> "$QUTE_FIFO"
diff --git a/.config/qutebrowser/misc/userscripts/getbib b/.config/qutebrowser/misc/userscripts/getbib
new file mode 100755
index 0000000..22af7a8
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/getbib
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+"""Qutebrowser userscript scraping the current web page for DOIs and downloading
+corresponding bibtex information.
+
+Set the environment variable 'QUTE_BIB_FILEPATH' to indicate the path to
+download to. Otherwise, bibtex information is downloaded to '/tmp' and hence
+deleted at reboot.
+
+Installation: see qute://help/userscripts.html
+
+Inspired by
+https://ocefpaf.github.io/python4oceanographers/blog/2014/05/19/doi2bibtex/
+"""
+
+import os
+import sys
+import shutil
+import re
+from collections import Counter
+from urllib import parse as url_parse
+from urllib import request as url_request
+
+
+FIFO_PATH = os.getenv("QUTE_FIFO")
+
+def message_fifo(message, level="warning"):
+ """Send message to qutebrowser FIFO. The level must be one of 'info',
+ 'warning' (default) or 'error'."""
+ with open(FIFO_PATH, "w") as fifo:
+ fifo.write("message-{} '{}'".format(level, message))
+
+
+source = os.getenv("QUTE_TEXT")
+with open(source) as f:
+ text = f.read()
+
+# find DOIs on page using regex
+dval = re.compile(r'(10\.(\d)+/([^(\s\>\"\<)])+)')
+# https://stackoverflow.com/a/10324802/3865876, too strict
+# dval = re.compile(r'\b(10[.][0-9]{4,}(?:[.][0-9]+)*/(?:(?!["&\'<>])\S)+)\b')
+dois = dval.findall(text)
+dois = Counter(e[0] for e in dois)
+try:
+ doi = dois.most_common(1)[0][0]
+except IndexError:
+ message_fifo("No DOIs found on page")
+ sys.exit()
+message_fifo("Found {} DOIs on page, selecting {}".format(len(dois), doi),
+ level="info")
+
+# get bibtex data corresponding to DOI
+url = "http://dx.doi.org/" + url_parse.quote(doi)
+headers = dict(Accept='text/bibliography; style=bibtex')
+request = url_request.Request(url, headers=headers)
+response = url_request.urlopen(request)
+status_code = response.getcode()
+if status_code >= 400:
+ message_fifo("Request returned {}".format(status_code))
+ sys.exit()
+
+# obtain content and format it
+bibtex = response.read().decode("utf-8").strip()
+bibtex = bibtex.replace(" ", "\n ", 1).\
+ replace("}, ", "},\n ").replace("}}", "}\n}")
+
+# append to file
+bib_filepath = os.getenv("QUTE_BIB_FILEPATH", "/tmp/qute.bib")
+with open(bib_filepath, "a") as f:
+ f.write(bibtex + "\n\n")
diff --git a/.config/qutebrowser/misc/userscripts/open_download b/.config/qutebrowser/misc/userscripts/open_download
new file mode 100755
index 0000000..8dbb113
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/open_download
@@ -0,0 +1,115 @@
+#!/usr/bin/env bash
+# Both standalone script and qutebrowser userscript that opens a rofi menu with
+# all files from the download director and opens the selected file. It works
+# both as a userscript and a standalone script that is called from outside of
+# qutebrowser.
+#
+# Suggested keybinding (for "show downloads"):
+# spawn --userscript ~/.config/qutebrowser/open_download
+# sd
+#
+# Requirements:
+# - rofi (in a recent version)
+# - xdg-open and xdg-mime
+# - You should configure qutebrowser to download files to a single directory
+# - It comes in handy if you enable downloads.remove_finished. If you want to
+# see the recent downloads, just press "sd".
+#
+# Thorsten Wißmann, 2015 (thorsten` on freenode)
+# Any feedback is welcome!
+
+set -e
+
+# open a file from the download directory using rofi
+DOWNLOAD_DIR=${DOWNLOAD_DIR:-$QUTE_DOWNLOAD_DIR}
+DOWNLOAD_DIR=${DOWNLOAD_DIR:-$HOME/Downloads}
+# the name of the rofi command
+ROFI_CMD=${ROFI_CMD:-rofi}
+ROFI_ARGS=${ROFI_ARGS:-}
+
+msg() {
+ local cmd="$1"
+ shift
+ local msg="$*"
+ if [ -z "$QUTE_FIFO" ] ; then
+ echo "$cmd: $msg" >&2
+ else
+ echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
+ fi
+}
+die() {
+ msg error "$*"
+ if [ -n "$QUTE_FIFO" ] ; then
+ # when run as a userscript, the above error message already informs the
+ # user about the failure, and no additional "userscript exited with status
+ # 1" is needed.
+ exit 0;
+ else
+ exit 1;
+ fi
+}
+
+if ! [ -d "$DOWNLOAD_DIR" ] ; then
+ die "Download directory »$DOWNLOAD_DIR« not found!"
+fi
+if ! command -v "${ROFI_CMD}" > /dev/null ; then
+ die "Rofi command »${ROFI_CMD}« not found in PATH!"
+fi
+
+rofi_default_args=(
+ -monitor -2 # place above window
+ -location 6 # aligned at the bottom
+ -width 100 # use full window width
+ -i
+ -no-custom
+ -format i # make rofi return the index
+ -l 10
+ -p 'Open download:' -dmenu
+ )
+
+crop-first-column() {
+ local maxlength=${1:-40}
+ local expression='s|^\([^\t]\{0,'"$maxlength"'\}\)[^\t]*\t|\1\t|'
+ sed "$expression"
+}
+
+ls-files() {
+ # add the slash at the end of the download dir enforces to follow the
+ # symlink, if the DOWNLOAD_DIR itself is a symlink
+ # shellcheck disable=SC2010
+ ls -Q --quoting-style escape -h -o -1 -A -t "${DOWNLOAD_DIR}/" \
+ | grep '^[-]' \
+ | cut -d' ' -f3- \
+ | sed 's,^\(.*[^\]\) \(.*\)$,\2\t\1,' \
+ | sed 's,\\\(.\),\1,g'
+}
+
+mapfile -t entries < <(ls-files)
+
+# we need to manually check that there are items, because rofi doesn't show up
+# if there are no items and -no-custom is passed to rofi.
+if [ "${#entries[@]}" -eq 0 ] ; then
+ die "Download directory »${DOWNLOAD_DIR}« empty"
+fi
+
+line=$(printf '%s\n' "${entries[@]}" \
+ | crop-first-column 55 \
+ | column -s $'\t' -t \
+ | $ROFI_CMD "${rofi_default_args[@]}" "$ROFI_ARGS") || true
+if [ -z "$line" ]; then
+ exit 0
+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
+
+msg info "Opening »$file« (of type $filetype) with ${application%.desktop}"
+
+xdg-open "$path" &
diff --git a/.config/qutebrowser/misc/userscripts/openfeeds b/.config/qutebrowser/misc/userscripts/openfeeds
new file mode 100755
index 0000000..4a1a942
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/openfeeds
@@ -0,0 +1,40 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# Copyright 2015 jnphilipp <me@jnphilipp.org>
+# Copyright 2016-2017 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 <http://www.gnu.org/licenses/>.
+
+# Opens all links to feeds defined in the head of a site
+#
+# Ideal for use with tabs_are_windows. Set a hotkey to launch this script, then:
+# :bind gF spawn --userscript openfeeds
+#
+# Use the hotkey to open the feeds in new tab/window, press 'gF' to open
+#
+
+import os
+import re
+
+from bs4 import BeautifulSoup
+from urllib.parse import urljoin
+
+with open(os.environ['QUTE_HTML'], 'r') as f:
+ soup = BeautifulSoup(f)
+with open(os.environ['QUTE_FIFO'], 'w') as f:
+ for link in soup.find_all('link', rel='alternate', type=re.compile(r'application/((rss|rdf|atom)\+)?xml|text/xml')):
+ f.write('open -t %s\n' % urljoin(os.environ['QUTE_URL'], link.get('href')))
diff --git a/.config/qutebrowser/misc/userscripts/password_fill b/.config/qutebrowser/misc/userscripts/password_fill
new file mode 100755
index 0000000..a61a42c
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/password_fill
@@ -0,0 +1,381 @@
+#!/usr/bin/env bash
+help() {
+ blink=$'\e[1;31m' reset=$'\e[0m'
+cat <<EOF
+This script can only be used as a userscript for qutebrowser
+2015, Thorsten Wißmann <edu _at_ thorsten-wissmann _dot_ de>
+In case of questions or suggestions, do not hesitate to send me an E-Mail or to
+directly ask me via IRC (nickname thorsten\`) in #qutebrowser on freenode.
+
+ $blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
+ WARNING: the passwords are stored in qutebrowser's
+ debug log reachable via the url qute://log
+ $blink!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!$reset
+
+Usage: run as a userscript form qutebrowser, e.g.:
+ spawn --userscript ~/.config/qutebrowser/password_fill
+
+Pass backend: (see also passwordstore.org)
+ This script expects pass to store the credentials of each page in an extra
+ file, where the filename (or filepath) contains the domain of the respective
+ page. The first line of the file must contain the password, the login name
+ must be contained in a later line beginning with "user:", "login:", or
+ "username:" (configurable by the user_pattern variable).
+
+Behavior:
+ It will try to find a username/password entry in the configured backend
+ (currently only pass) for the current website and will load that pair of
+ username and password to any form on the current page that has some password
+ entry field. If multiple entries are found, a zenity menu is offered.
+
+ If no entry is found, then it crops subdomains from the url if at least one
+ entry is found in the backend. (In that case, it always shows a menu)
+
+Configuration:
+ This script loads the bash script ~/.config/qutebrowser/password_fill_rc (if
+ it exists), so you can change any configuration variable and overwrite any
+ function you like.
+
+EOF
+}
+
+set -o errexit
+set -o pipefail
+shopt -s nocasematch # make regexp matching in bash case insensitive
+
+if [ -z "$QUTE_FIFO" ] ; then
+ help
+ exit
+fi
+
+error() {
+ local msg="$*"
+ echo "message-error '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
+}
+msg() {
+ local msg="$*"
+ echo "message-info '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
+}
+die() {
+ error "$*"
+ exit 0
+}
+
+javascript_escape() {
+ # print the first argument in an escaped way, such that it can safely
+ # be used within javascripts double quotes
+ sed "s,[\\\\'\"],\\\\&,g" <<< "$1"
+}
+
+# ======================================================= #
+# CONFIGURATION
+# ======================================================= #
+# The configuration file is per default located in
+# ~/.config/qutebrowser/password_fill_rc and is a bash script that is loaded
+# later in the present script. So basically you can replace all of the
+# following definitions and make them fit your needs.
+
+# The following simplifies a URL to the domain (e.g. "wiki.qutebrowser.org")
+# which is later used to search the correct entries in the password backend. If
+# you e.g. don't want the "www." to be removed or if you want to distinguish
+# between different paths on the same domain.
+
+simplify_url() {
+ simple_url="${1##*://}" # remove protocol specification
+ simple_url="${simple_url%%\?*}" # remove GET parameters
+ simple_url="${simple_url%%/*}" # remove directory path
+ simple_url="${simple_url%:*}" # remove port
+ simple_url="${simple_url##www.}" # remove www. subdomain
+}
+
+# no_entries_found() is called if the first query_entries() call did not find
+# any matching entries. Multiple implementations are possible:
+# The easiest behavior is to quit:
+#no_entries_found() {
+# if [ 0 -eq "${#files[@]}" ] ; then
+# die "No entry found for »$simple_url«"
+# fi
+#}
+# But you could also fill the files array with all entries from your pass db
+# if the first db query did not find anything
+# no_entries_found() {
+# if [ 0 -eq "${#files[@]}" ] ; then
+# query_entries ""
+# if [ 0 -eq "${#files[@]}" ] ; then
+# die "No entry found for »$simple_url«"
+# fi
+# fi
+# }
+
+# Another behavior is to drop another level of subdomains until search hits
+# are found:
+no_entries_found() {
+ while [ 0 -eq "${#files[@]}" ] && [ -n "$simple_url" ]; do
+ shorter_simple_url=$(sed 's,^[^.]*\.,,' <<< "$simple_url")
+ if [ "$shorter_simple_url" = "$simple_url" ] ; then
+ # if no dot, then even remove the top level domain
+ simple_url=""
+ query_entries "$simple_url"
+ break
+ fi
+ simple_url="$shorter_simple_url"
+ query_entries "$simple_url"
+ #die "No entry found for »$simple_url«"
+ # enforce menu if we do "fuzzy" matching
+ menu_if_one_entry=1
+ done
+ if [ 0 -eq "${#files[@]}" ] ; then
+ die "No entry found for »$simple_url«"
+ fi
+}
+
+# Backend implementations tell, how the actual password store is accessed.
+# Right now, there is only one fully functional password backend, namely for
+# the program "pass".
+# A password backend consists of three actions:
+# - init() initializes backend-specific things and does sanity checks.
+# - query_entries() is called with a simplified url and is expected to fill
+# the bash array $files with the names of matching password entries. There
+# are no requirements how these names should look like.
+# - open_entry() is called with some specific entry of the $files array and is
+# expected to write the username of that entry to the $username variable and
+# the corresponding password to $password
+
+reset_backend() {
+ init() { true ; }
+ query_entries() { true ; }
+ open_entry() { true ; }
+}
+
+# choose_entry() is expected to choose one entry from the array $files and
+# write it to the variable $file.
+choose_entry() {
+ choose_entry_zenity
+}
+
+# The default implementation chooses a random entry from the array. So if there
+# are multiple matching entries, multiple calls to this userscript will
+# eventually pick the "correct" entry. I.e. if this userscript is bound to
+# "zl", the user has to press "zl" until the correct username shows up in the
+# login form.
+choose_entry_random() {
+ local nr=${#files[@]}
+ file="${files[$((RANDOM % nr))]}"
+ # Warn user, that there might be other matching password entries
+ if [ "$nr" -gt 1 ] ; then
+ msg "Picked $file out of $nr entries: ${files[*]}"
+ fi
+}
+
+# another implementation would be to ask the user via some menu (like rofi or
+# dmenu or zenity or even qutebrowser completion in future?) which entry to
+# pick
+MENU_COMMAND=( head -n 1 )
+# whether to show the menu if there is only one entry in it
+menu_if_one_entry=0
+choose_entry_menu() {
+ local nr=${#files[@]}
+ if [ "$nr" -eq 1 ] && ! ((menu_if_one_entry)) ; then
+ file="${files[0]}"
+ else
+ file=$( printf '%s\n' "${files[@]}" | "${MENU_COMMAND[@]}" )
+ fi
+}
+
+choose_entry_rofi() {
+ MENU_COMMAND=( rofi -p "qutebrowser> " -dmenu
+ -mesg $'Pick a password entry for <b>'"${QUTE_URL//&/&amp;}"'</b>' )
+ choose_entry_menu || true
+}
+
+choose_entry_zenity() {
+ MENU_COMMAND=( zenity --list --title "qutebrowser password fill"
+ --text "Pick the password entry:"
+ --column "Name" )
+ choose_entry_menu || true
+}
+
+choose_entry_zenity_radio() {
+ zenity_helper() {
+ awk '{ print $0 ; print $0 }' \
+ | zenity --list --radiolist \
+ --title "qutebrowser password fill" \
+ --text "Pick the password entry:" \
+ --column " " --column "Name"
+ }
+ MENU_COMMAND=( zenity_helper )
+ choose_entry_menu || true
+}
+
+# =======================================================
+# backend: PASS
+
+# configuration options:
+match_filename=1 # whether allowing entry match by filepath
+match_line=0 # whether allowing entry match by URL-Pattern in file
+ # Note: match_line=1 gets very slow, even for small password stores!
+match_line_pattern='^url: .*' # applied using grep -iE
+user_pattern='^(user|username|login): '
+
+GPG_OPTS=( "--quiet" "--yes" "--compress-algo=none" "--no-encrypt-to" )
+GPG="gpg"
+export GPG_TTY="${GPG_TTY:-$(tty 2>/dev/null)}"
+command -v gpg2 &>/dev/null && GPG="gpg2"
+[[ -n $GPG_AGENT_INFO || $GPG == "gpg2" ]] && GPG_OPTS+=( "--batch" "--use-agent" )
+
+pass_backend() {
+ init() {
+ PREFIX="${PASSWORD_STORE_DIR:-$HOME/.password-store}"
+ if ! [ -d "$PREFIX" ] ; then
+ die "Can not open password store dir »$PREFIX«"
+ fi
+ }
+ query_entries() {
+ local url="$1"
+
+ if ((match_line)) ; then
+ # add entries with matching URL-tag
+ while read -r -d "" passfile ; do
+ if $GPG "${GPG_OPTS[@]}" -d "$passfile" \
+ | grep --max-count=1 -iE "${match_line_pattern}${url}" > /dev/null
+ then
+ passfile="${passfile#$PREFIX}"
+ passfile="${passfile#/}"
+ files+=( "${passfile%.gpg}" )
+ fi
+ done < <(find -L "$PREFIX" -iname '*.gpg' -print0)
+ fi
+ if ((match_filename)) ; then
+ # add entries with matching filepath
+ while read -r passfile ; do
+ passfile="${passfile#$PREFIX}"
+ passfile="${passfile#/}"
+ files+=( "${passfile%.gpg}" )
+ done < <(find -L "$PREFIX" -iname '*.gpg' | grep "$url")
+ fi
+ }
+ open_entry() {
+ local path="$PREFIX/${1}.gpg"
+ password=""
+ local firstline=1
+ while read -r line ; do
+ if ((firstline)) ; then
+ password="$line"
+ firstline=0
+ else
+ if [[ $line =~ $user_pattern ]] ; then
+ # remove the matching prefix "user: " from the beginning of the line
+ username=${line#${BASH_REMATCH[0]}}
+ break
+ fi
+ fi
+ done < <($GPG "${GPG_OPTS[@]}" -d "$path" )
+ }
+}
+# =======================================================
+
+# =======================================================
+# backend: secret
+secret_backend() {
+ init() {
+ return
+ }
+ query_entries() {
+ local domain="$1"
+ while read -r line ; do
+ if [[ "$line" == "attribute.username = "* ]] ; then
+ files+=("$domain ${line:21}")
+ fi
+ done < <( secret-tool search --unlock --all domain "$domain" 2>&1 )
+ }
+ open_entry() {
+ local domain="${1%% *}"
+ username="${1#* }"
+ password=$(secret-tool lookup domain "$domain" username "$username")
+ }
+}
+# =======================================================
+
+# load some sane default backend
+reset_backend
+pass_backend
+# load configuration
+QUTE_CONFIG_DIR=${QUTE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/qutebrowser/}
+PWFILL_CONFIG=${PWFILL_CONFIG:-${QUTE_CONFIG_DIR}/password_fill_rc}
+if [ -f "$PWFILL_CONFIG" ] ; then
+ # shellcheck source=/dev/null
+ source "$PWFILL_CONFIG"
+fi
+init
+
+simplify_url "$QUTE_URL"
+query_entries "${simple_url}"
+no_entries_found
+# remove duplicates
+mapfile -t files < <(printf '%s\n' "${files[@]}" | sort | uniq )
+choose_entry
+if [ -z "$file" ] ; then
+ # choose_entry didn't want any of these entries
+ exit 0
+fi
+open_entry "$file"
+#username="$(date)"
+#password="XYZ"
+#msg "$username, ${#password}"
+
+[ -n "$username" ] || die "Username not set in entry $file"
+[ -n "$password" ] || die "Password not set in entry $file"
+
+js() {
+cat <<EOF
+ function isVisible(elem) {
+ var style = elem.ownerDocument.defaultView.getComputedStyle(elem, null);
+
+ if (style.getPropertyValue("visibility") !== "visible" ||
+ style.getPropertyValue("display") === "none" ||
+ style.getPropertyValue("opacity") === "0") {
+ return false;
+ }
+
+ return elem.offsetWidth > 0 && elem.offsetHeight > 0;
+ };
+ function hasPasswordField(form) {
+ var inputs = form.getElementsByTagName("input");
+ for (var j = 0; j < inputs.length; j++) {
+ var input = inputs[j];
+ if (input.type == "password") {
+ return true;
+ }
+ }
+ return false;
+ };
+ function loadData2Form (form) {
+ var inputs = form.getElementsByTagName("input");
+ for (var j = 0; j < inputs.length; j++) {
+ var input = inputs[j];
+ if (isVisible(input) && (input.type == "text" || input.type == "email")) {
+ input.focus();
+ input.value = "$(javascript_escape "${username}")";
+ input.blur();
+ }
+ if (input.type == "password") {
+ input.focus();
+ input.value = "$(javascript_escape "${password}")";
+ input.blur();
+ }
+ }
+ };
+
+ var forms = document.getElementsByTagName("form");
+ for (i = 0; i < forms.length; i++) {
+ if (hasPasswordField(forms[i])) {
+ loadData2Form(forms[i]);
+ }
+ }
+EOF
+}
+
+printjs() {
+ js | sed 's,//.*$,,' | tr '\n' ' '
+}
+echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
diff --git a/.config/qutebrowser/misc/userscripts/qute-keepass b/.config/qutebrowser/misc/userscripts/qute-keepass
new file mode 100755
index 0000000..a21ebc9
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/qute-keepass
@@ -0,0 +1,261 @@
+#!/usr/bin/env python3
+
+# Copyright 2018 Jay Kamat <jaygkamat@gmail.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+"""This userscript allows for insertion of usernames and passwords from keepass
+databases using pykeepass. Since it is a userscript, it must be run from
+qutebrowser.
+
+A sample invocation of this script is:
+
+:spawn --userscript qute-keepass -p ~/KeePassFiles/MainDatabase.kdbx
+
+And a sample binding
+
+:bind --mode=insert <ctrl-i> spawn --userscript qute-keepass -p ~/KeePassFiles/MainDatabase.kdbx
+
+-p or --path is a required argument.
+
+--keyfile-path allows you to specify a keepass keyfile. If you only use a
+keyfile, also add --no-password as well. Specifying --no-password without
+--keyfile-path will lead to an error.
+
+login information is inserted using :insert-text and :fake-key <Tab>, which
+means you must have a cursor in position before initiating this userscript. If
+you do not do this, you will get 'element not editable' errors.
+
+If keepass takes a while to open the DB, you might want to consider reducing
+the number of transform rounds in your database settings.
+
+Dependencies: pykeepass (in python3), PyQt5. Without pykeepass, you will get an
+exit code of 100.
+
+********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!******************
+
+WARNING: The login details are viewable as plaintext in qutebrowser's debug log
+(qute://log) and could be compromised if you decide to submit a crash report!
+
+********************!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!******************
+
+"""
+
+# pylint: disable=bad-builtin
+
+import argparse
+import enum
+import functools
+import os
+import shlex
+import subprocess
+import sys
+
+from PyQt5.QtCore import QUrl
+from PyQt5.QtWidgets import QApplication, QInputDialog, QLineEdit
+
+try:
+ import pykeepass
+except ImportError as e:
+ print("pykeepass not found: {}".format(str(e)), file=sys.stderr)
+
+ # Since this is a common error, try to print it to the FIFO if we can.
+ if 'QUTE_FIFO' in os.environ:
+ with open(os.environ['QUTE_FIFO'], 'w') as fifo:
+ fifo.write('message-error "pykeepass failed to be imported."\n')
+ fifo.flush()
+ sys.exit(100)
+
+argument_parser = argparse.ArgumentParser(
+ description="Fill passwords using keepass.",
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ epilog=__doc__)
+argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
+argument_parser.add_argument('--path', '-p', required=True,
+ help='Path to the keepass db.')
+argument_parser.add_argument('--keyfile-path', '-k', default=None,
+ help='Path to a keepass keyfile')
+argument_parser.add_argument(
+ '--no-password', action='store_true',
+ help='Supply if no password is required to unlock this database. '
+ 'Only allowed with --keyfile-path')
+argument_parser.add_argument(
+ '--dmenu-invocation', '-d', default='dmenu',
+ help='Invocation used to execute a dmenu-provider')
+argument_parser.add_argument(
+ '--dmenu-format', '-f', default='{title}: {username}',
+ help='Format string for keys to display in dmenu.'
+ ' Must generate a unique string.')
+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(
+ '--io-encoding', '-i', default='UTF-8',
+ help='Encoding used to communicate with subprocesses')
+group = argument_parser.add_mutually_exclusive_group()
+group.add_argument('--username-fill-only', '-e',
+ action='store_true', help='Only insert username')
+group.add_argument('--password-fill-only', '-w',
+ action='store_true', help='Only insert password')
+
+CMD_DELAY = 50
+
+
+class ExitCodes(enum.IntEnum):
+ """Stores various exit codes groups to use."""
+ SUCCESS = 0
+ FAILURE = 1
+ # 1 is automatically used if Python throws an exception
+ NO_CANDIDATES = 2
+ USER_QUIT = 3
+ DB_OPEN_FAIL = 4
+
+ INTERNAL_ERROR = 10
+
+
+def qute_command(command):
+ with open(os.environ['QUTE_FIFO'], 'w') as fifo:
+ fifo.write(command + '\n')
+ fifo.flush()
+
+
+def stderr(to_print):
+ """Extra functionality to echo out errors to qb ui."""
+ print(to_print, file=sys.stderr)
+ qute_command('message-error "{}"'.format(to_print))
+
+
+def dmenu(items, invocation, encoding):
+ """Runs dmenu with given arguments."""
+ command = shlex.split(invocation)
+ process = subprocess.run(command, input='\n'.join(items).encode(encoding),
+ stdout=subprocess.PIPE)
+ return process.stdout.decode(encoding).strip()
+
+
+def get_password():
+ """Get a keepass db password from user."""
+ _app = QApplication(sys.argv)
+ text, ok = QInputDialog.getText(
+ None, "KeePass DB Password",
+ "Please enter your KeePass Master Password",
+ QLineEdit.Password)
+ if not ok:
+ stderr('Password Prompt Rejected.')
+ sys.exit(ExitCodes.USER_QUIT)
+ return text
+
+
+def find_candidates(args, host):
+ """Finds candidates that match host"""
+ file_path = os.path.expanduser(args.path)
+
+ # TODO find a way to keep the db open, so we don't open (and query
+ # password) it every time
+
+ pw = None
+ if not args.no_password:
+ pw = get_password()
+
+ kf = args.keyfile_path
+ if kf:
+ kf = os.path.expanduser(kf)
+
+ try:
+ kp = pykeepass.PyKeePass(file_path, password=pw, keyfile=kf)
+ except Exception as e:
+ stderr("There was an error opening the DB: {}".format(str(e)))
+
+ return kp.find_entries(url="{}{}{}".format(".*", host, ".*"), regex=True)
+
+
+def candidate_to_str(args, candidate):
+ """Turns candidate into a human readable string for dmenu"""
+ return args.dmenu_format.format(title=candidate.title,
+ url=candidate.url,
+ username=candidate.username,
+ path=candidate.path,
+ uuid=candidate.uuid)
+
+
+def candidate_to_secret(candidate):
+ """Turns candidate into a generic (user, password) tuple"""
+ return (candidate.username, candidate.password)
+
+
+def run(args):
+ """Runs qute-keepass"""
+ if not args.url:
+ argument_parser.print_help()
+ return ExitCodes.FAILURE
+
+ url_host = QUrl(args.url).host()
+
+ if not url_host:
+ stderr('{} was not parsed as a valid URL!'.format(args.url))
+ return ExitCodes.INTERNAL_ERROR
+
+ # Find candidates matching the host of the given URL
+ candidates = find_candidates(args, url_host)
+ if not candidates:
+ stderr('No candidates for URL {!r} found!'.format(args.url))
+ return ExitCodes.NO_CANDIDATES
+
+ # Create a map so we can get turn the resulting string from dmenu back into
+ # a candidate
+ candidates_strs = list(map(functools.partial(candidate_to_str, args),
+ candidates))
+ candidates_map = dict(zip(candidates_strs, candidates))
+
+ if len(candidates) == 1:
+ selection = candidates.pop()
+ else:
+ selection = dmenu(candidates_strs,
+ args.dmenu_invocation,
+ args.io_encoding)
+
+ if selection not in candidates_map:
+ stderr("'{}' was not a valid entry!").format(selection)
+ return ExitCodes.USER_QUIT
+
+ selection = candidates_map[selection]
+
+ username, password = candidate_to_secret(selection)
+
+ insert_mode = ';; enter-mode insert' if args.insert_mode else ''
+ if args.username_fill_only:
+ qute_command('insert-text {}{}'.format(username, insert_mode))
+ elif args.password_fill_only:
+ qute_command('insert-text {}{}'.format(password, insert_mode))
+ else:
+ # Enter username and password using insert-key and fake-key <Tab>
+ # (which supports more passwords than fake-key only), then switch back
+ # into insert-mode, so the form can be directly submitted by hitting
+ # enter afterwards. It dosen't matter when we go into insert mode, but
+ # the other commands need to be be executed sequentially, so we add
+ # delays with later.
+ qute_command('insert-text {} ;;'
+ 'later {} fake-key <Tab> ;;'
+ 'later {} insert-text {}{}'
+ .format(username, CMD_DELAY,
+ CMD_DELAY * 2, password, insert_mode))
+
+ return ExitCodes.SUCCESS
+
+
+if __name__ == '__main__':
+ arguments = argument_parser.parse_args()
+ sys.exit(run(arguments))
diff --git a/.config/qutebrowser/misc/userscripts/qute-lastpass b/.config/qutebrowser/misc/userscripts/qute-lastpass
new file mode 100755
index 0000000..ea88cf8
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/qute-lastpass
@@ -0,0 +1,172 @@
+#!/usr/bin/env python3
+
+# Copyright 2017 Chris Braun (cryzed) <cryzed@googlemail.com>
+# Adapted for LastPass by Wayne Cheng (welps) <waynethecheng@gmail.com>
+#
+# 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
+# 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 <http://www.gnu.org/licenses/>.
+
+"""
+Insert login information using lastpass CLI and a dmenu-compatible application (e.g. dmenu, rofi -dmenu, ...).
+A short demonstration can be seen here: https://i.imgur.com/zA61NrF.gifv.
+"""
+
+USAGE = """The domain of the site has to be in the name of the LastPass entry, for example: "github.com/cryzed" or
+"websites/github.com". The login information is inserted by emulating key events using qutebrowser's fake-key command in this manner:
+[USERNAME]<Tab>[PASSWORD], which is compatible with almost all login forms.
+
+You must log into LastPass CLI using `lpass login <email>` prior to use of this script. The LastPass CLI agent only holds your master password for an hour by default. If you wish to change this, please see `man lpass`.
+
+To use in qutebrowser, run: `spawn --userscript qute-lastpass`
+"""
+
+EPILOG = """Dependencies: tldextract (Python 3 module), LastPass CLI (1.3 or newer)
+
+WARNING: The login details are viewable as plaintext in qutebrowser's debug log (qute://log) and might be shared if
+you decide to submit a crash report!"""
+
+import argparse
+import enum
+import fnmatch
+import functools
+import os
+import re
+import shlex
+import subprocess
+import sys
+import json
+import tldextract
+
+argument_parser = argparse.ArgumentParser(
+ description=__doc__, usage=USAGE, epilog=EPILOG)
+argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
+argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu',
+ help='Invocation used to execute a dmenu-provider')
+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('--io-encoding', '-i', default='UTF-8',
+ help='Encoding used to communicate with subprocesses')
+argument_parser.add_argument('--merge-candidates', '-m', action='store_true',
+ help='Merge pass candidates for fully-qualified and registered domain name')
+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')
+
+stderr = functools.partial(print, file=sys.stderr)
+
+class ExitCodes(enum.IntEnum):
+ SUCCESS = 0
+ FAILURE = 1
+ # 1 is automatically used if Python throws an exception
+ NO_PASS_CANDIDATES = 2
+ COULD_NOT_MATCH_USERNAME = 3
+ COULD_NOT_MATCH_PASSWORD = 4
+
+def qute_command(command):
+ with open(os.environ['QUTE_FIFO'], 'w') as fifo:
+ fifo.write(command + '\n')
+ fifo.flush()
+
+def pass_(domain, encoding):
+ args = ['lpass', 'show', '-x', '-j', '-G', '.*{:s}.*'.format(domain)]
+ process = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+
+ err = process.stderr.decode(encoding).strip()
+ if err:
+ msg = "LastPass CLI returned for {:s} - {:s}".format(domain, err)
+ stderr(msg)
+ return '[]'
+
+ out = process.stdout.decode(encoding).strip()
+
+ return out
+
+def dmenu(items, invocation, encoding):
+ command = shlex.split(invocation)
+ process = subprocess.run(command, input='\n'.join(
+ items).encode(encoding), stdout=subprocess.PIPE)
+ return process.stdout.decode(encoding).strip()
+
+
+def fake_key_raw(text):
+ for character in text:
+ # Escape all characters by default, space requires special handling
+ sequence = '" "' if character == ' ' else '\{}'.format(character)
+ qute_command('fake-key {}'.format(sequence))
+
+
+def main(arguments):
+ if not arguments.url:
+ argument_parser.print_help()
+ return ExitCodes.FAILURE
+
+ extract_result = tldextract.extract(arguments.url)
+
+ # Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains),
+ # the registered domain name and finally: the IPv4 address if that's what
+ # the URL represents
+ candidates = []
+ for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.subdomain + extract_result.domain, extract_result.domain, extract_result.ipv4]):
+ target_candidates = json.loads(pass_(target, arguments.io_encoding))
+ if not target_candidates:
+ continue
+
+ candidates = candidates + target_candidates
+ if not arguments.merge_candidates:
+ break
+ else:
+ if not candidates:
+ stderr('No pass candidates for URL {!r} found!'.format(
+ arguments.url))
+ return ExitCodes.NO_PASS_CANDIDATES
+
+ if len(candidates) == 1:
+ selection = candidates.pop()
+ else:
+ choices = ["{:s} | {:s} | {:s} | {:s}".format(c["id"], c["name"], c["url"], c["username"]) for c in candidates]
+ choice = dmenu(choices, arguments.dmenu_invocation, arguments.io_encoding)
+ choiceId = choice.split("|")[0].strip()
+ selection = next((c for (i, c) in enumerate(candidates) if c["id"] == choiceId), None)
+
+ # Nothing was selected, simply return
+ if not selection:
+ return ExitCodes.SUCCESS
+
+ username = selection["username"]
+ password = selection["password"]
+
+ if arguments.username_only:
+ fake_key_raw(username)
+ elif arguments.password_only:
+ fake_key_raw(password)
+ else:
+ # Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch
+ # back into insert-mode, so the form can be directly submitted by
+ # hitting enter afterwards
+ fake_key_raw(username)
+ qute_command('fake-key <Tab>')
+ fake_key_raw(password)
+
+ if arguments.insert_mode:
+ qute_command('enter-mode insert')
+
+ return ExitCodes.SUCCESS
+
+
+if __name__ == '__main__':
+ arguments = argument_parser.parse_args()
+ sys.exit(main(arguments))
diff --git a/.config/qutebrowser/misc/userscripts/qute-pass b/.config/qutebrowser/misc/userscripts/qute-pass
new file mode 100755
index 0000000..4f79e11
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/qute-pass
@@ -0,0 +1,207 @@
+#!/usr/bin/env python3
+
+# Copyright 2017 Chris Braun (cryzed) <cryzed@googlemail.com>
+#
+# 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 <http://www.gnu.org/licenses/>.
+
+"""
+Insert login information using pass and a dmenu-compatible application (e.g. dmenu, rofi -dmenu, ...). A short
+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. The
+login information is inserted by emulating key events using qutebrowser's fake-key command in this manner:
+[USERNAME]<Tab>[PASSWORD], which is compatible with almost all login forms.
+
+Suggested bindings similar to Uzbl's `formfiller` script:
+
+ config.bind('<z><l>', 'spawn --userscript qute-pass')
+ config.bind('<z><u><l>', 'spawn --userscript qute-pass --username-only')
+ config.bind('<z><p><l>', 'spawn --userscript qute-pass --password-only')
+ config.bind('<z><o><l>', 'spawn --userscript qute-pass --otp-only')
+"""
+
+EPILOG = """Dependencies: tldextract (Python 3 module), pass, pass-otp (optional).
+For issues and feedback please use: https://github.com/cryzed/qutebrowser-userscripts.
+
+WARNING: The login details are viewable as plaintext in qutebrowser's debug log (qute://log) and might be shared if
+you decide to submit a crash report!"""
+
+import argparse
+import enum
+import fnmatch
+import functools
+import os
+import re
+import shlex
+import subprocess
+import sys
+
+import tldextract
+
+argument_parser = argparse.ArgumentParser(description=__doc__, usage=USAGE, epilog=EPILOG)
+argument_parser.add_argument('url', nargs='?', default=os.getenv('QUTE_URL'))
+argument_parser.add_argument('--password-store', '-p', default=os.path.expanduser('~/.password-store'),
+ help='Path to your pass password-store')
+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',
+ help='The target for the username regular expression')
+argument_parser.add_argument('--password-pattern', '-P', default=r'(.*)',
+ help='Regular expression that matches the password')
+argument_parser.add_argument('--dmenu-invocation', '-d', default='rofi -dmenu',
+ help='Invocation used to execute a dmenu-provider')
+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('--io-encoding', '-i', default='UTF-8',
+ help='Encoding used to communicate with subprocesses')
+argument_parser.add_argument('--merge-candidates', '-m', action='store_true',
+ help='Merge pass candidates for fully-qualified and registered domain name')
+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')
+group.add_argument('--otp-only', '-o', action='store_true', help='Only insert OTP code')
+
+stderr = functools.partial(print, file=sys.stderr)
+
+
+class ExitCodes(enum.IntEnum):
+ SUCCESS = 0
+ FAILURE = 1
+ # 1 is automatically used if Python throws an exception
+ NO_PASS_CANDIDATES = 2
+ COULD_NOT_MATCH_USERNAME = 3
+ COULD_NOT_MATCH_PASSWORD = 4
+
+
+def qute_command(command):
+ with open(os.environ['QUTE_FIFO'], 'w') as fifo:
+ fifo.write(command + '\n')
+ fifo.flush()
+
+
+def find_pass_candidates(domain, password_store_path):
+ candidates = []
+ for path, directories, file_names in os.walk(password_store_path, followlinks=True):
+ if directories or domain not in path.split(os.path.sep):
+ continue
+
+ # Strip password store path prefix to get the relative pass path
+ pass_path = path[len(password_store_path) + 1:]
+ secrets = fnmatch.filter(file_names, '*.gpg')
+ candidates.extend(os.path.join(pass_path, os.path.splitext(secret)[0]) for secret in secrets)
+ return candidates
+
+
+def _run_pass(command, encoding):
+ process = subprocess.run(command, stdout=subprocess.PIPE)
+ return process.stdout.decode(encoding).strip()
+
+
+def pass_(path, encoding):
+ return _run_pass(['pass', path], encoding)
+
+
+def pass_otp(path, encoding):
+ return _run_pass(['pass', 'otp', path], encoding)
+
+
+def dmenu(items, invocation, encoding):
+ command = shlex.split(invocation)
+ process = subprocess.run(command, input='\n'.join(items).encode(encoding), stdout=subprocess.PIPE)
+ return process.stdout.decode(encoding).strip()
+
+
+def fake_key_raw(text):
+ for character in text:
+ # Escape all characters by default, space requires special handling
+ sequence = '" "' if character == ' ' else '\{}'.format(character)
+ qute_command('fake-key {}'.format(sequence))
+
+
+def main(arguments):
+ if not arguments.url:
+ argument_parser.print_help()
+ return ExitCodes.FAILURE
+
+ extract_result = tldextract.extract(arguments.url)
+
+ # Expand potential ~ in paths, since this script won't be called from a shell that does it for us
+ password_store_path = os.path.expanduser(arguments.password_store)
+
+ # Try to find candidates using targets in the following order: fully-qualified domain name (includes subdomains),
+ # the registered domain name and finally: the IPv4 address if that's what the URL represents
+ candidates = set()
+ for target in filter(None, [extract_result.fqdn, extract_result.registered_domain, extract_result.ipv4]):
+ target_candidates = find_pass_candidates(target, password_store_path)
+ if not target_candidates:
+ continue
+
+ candidates.update(target_candidates)
+ if not arguments.merge_candidates:
+ break
+ else:
+ if not candidates:
+ stderr('No pass candidates for URL {!r} found!'.format(arguments.url))
+ return ExitCodes.NO_PASS_CANDIDATES
+
+ selection = candidates.pop() if len(candidates) == 1 else dmenu(sorted(candidates), arguments.dmenu_invocation,
+ arguments.io_encoding)
+ # Nothing was selected, simply return
+ if not selection:
+ return ExitCodes.SUCCESS
+
+ secret = pass_(selection, arguments.io_encoding)
+
+ # Match username
+ target = selection if arguments.username_target == 'path' else secret
+ match = re.match(arguments.username_pattern, target)
+ if not match:
+ stderr('Failed to match username pattern on {}!'.format(arguments.username_target))
+ return ExitCodes.COULD_NOT_MATCH_USERNAME
+ username = match.group(1)
+
+ # Match password
+ match = re.match(arguments.password_pattern, secret)
+ if not match:
+ stderr('Failed to match password pattern on secret!')
+ return ExitCodes.COULD_NOT_MATCH_PASSWORD
+ password = match.group(1)
+
+ if arguments.username_only:
+ fake_key_raw(username)
+ elif arguments.password_only:
+ fake_key_raw(password)
+ elif arguments.otp_only:
+ otp = pass_otp(selection, arguments.io_encoding)
+ fake_key_raw(otp)
+ else:
+ # Enter username and password using fake-key and <Tab> (which seems to work almost universally), then switch
+ # back into insert-mode, so the form can be directly submitted by hitting enter afterwards
+ fake_key_raw(username)
+ qute_command('fake-key <Tab>')
+ fake_key_raw(password)
+
+ if arguments.insert_mode:
+ qute_command('enter-mode insert')
+
+ return ExitCodes.SUCCESS
+
+
+if __name__ == '__main__':
+ arguments = argument_parser.parse_args()
+ sys.exit(main(arguments))
diff --git a/.config/qutebrowser/misc/userscripts/qutedmenu b/.config/qutebrowser/misc/userscripts/qutedmenu
new file mode 100755
index 0000000..de1b8d6
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/qutedmenu
@@ -0,0 +1,54 @@
+#!/usr/bin/env bash
+# Handle open -s && open -t with bemenu
+
+#:bind o spawn --userscript /path/to/userscripts/qutedmenu open
+#:bind O spawn --userscript /path/to/userscripts/qutedmenu tab
+
+# If you would like to set a custom colorscheme/font use these dirs.
+# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/bemenucolors
+readonly confdir=${XDG_CONFIG_HOME:-$HOME/.config}
+
+readonly optsfile=$confdir/dmenu/bemenucolors
+
+create_menu() {
+ # Check quickmarks
+ while read -r url; do
+ printf -- '%s\n' "$url"
+ done < "$QUTE_CONFIG_DIR"/quickmarks
+
+ # Next bookmarks
+ while read -r url _; do
+ printf -- '%s\n' "$url"
+ done < "$QUTE_CONFIG_DIR"/bookmarks/urls
+
+ # Finally history
+ while read -r _ url; do
+ printf -- '%s\n' "$url"
+ done < "$QUTE_DATA_DIR"/history
+ }
+
+get_selection() {
+ opts+=(-p qutebrowser)
+ #create_menu | dmenu -l 10 "${opts[@]}"
+ create_menu | bemenu -l 10 "${opts[@]}"
+}
+
+# Main
+# https://github.com/halfwit/dotfiles/blob/master/.config/dmenu/font
+[[ -s $confdir/dmenu/font ]] && read -r font < "$confdir"/dmenu/font
+
+[[ $font ]] && opts+=(-fn "$font")
+
+# shellcheck source=/dev/null
+[[ -s $optsfile ]] && source "$optsfile"
+
+url=$(get_selection)
+url=${url/*http/http}
+
+# If no selection is made, exit (escape pressed, e.g.)
+[[ ! $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" ;;
+esac
diff --git a/.config/qutebrowser/misc/userscripts/readability b/.config/qutebrowser/misc/userscripts/readability
new file mode 100755
index 0000000..d0ef437
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/readability
@@ -0,0 +1,41 @@
+#!/usr/bin/env python
+#
+# Executes python-readability on current page and opens the summary as new tab.
+#
+# Depends on the python-readability package, or its fork:
+#
+# - https://github.com/buriy/python-readability
+# - https://github.com/bookieio/breadability
+#
+# Usage:
+# :spawn --userscript readability
+#
+from __future__ import absolute_import
+import codecs, os
+
+tmpfile = os.path.join(
+ os.environ.get('QUTE_DATA_DIR',
+ os.path.expanduser('~/.local/share/qutebrowser')),
+ 'userscripts/readability.html')
+
+if not os.path.exists(os.path.dirname(tmpfile)):
+ os.makedirs(os.path.dirname(tmpfile))
+
+with codecs.open(os.environ['QUTE_HTML'], 'r', 'utf-8') as source:
+ data = source.read()
+
+ try:
+ from breadability.readable import Article as reader
+ doc = reader(data)
+ content = doc.readable
+ except ImportError:
+ from readability import Document
+ doc = Document(data)
+ content = doc.summary().replace('<html>', '<html><head><title>%s</title></head>' % doc.title())
+
+ with codecs.open(tmpfile, 'w', 'utf-8') as target:
+ target.write('<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />')
+ target.write(content)
+
+ with open(os.environ['QUTE_FIFO'], 'w') as fifo:
+ fifo.write('open -t %s' % tmpfile)
diff --git a/.config/qutebrowser/misc/userscripts/ripbang b/.config/qutebrowser/misc/userscripts/ripbang
new file mode 100755
index 0000000..b35ff77
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/ripbang
@@ -0,0 +1,34 @@
+#!/usr/bin/env python
+#
+# Adds DuckDuckGo bang as searchengine.
+#
+# Usage:
+# :spawn --userscript ripbang [bang]...
+#
+# Example:
+# :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
+
+for argument in sys.argv[1:]:
+ bang = '!' + argument
+ r = requests.get('https://duckduckgo.com/',
+ params={'q': bang + ' SEARCHTEXT'})
+
+ searchengine = unquote(re.search("url=[^']+", r.text).group(0))
+ searchengine = searchengine.replace('url=', '')
+ searchengine = searchengine.replace('/l/?kh=-1&uddg=', '')
+ 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))
+ else:
+ print('%s %s' % (bang, searchengine))
diff --git a/.config/qutebrowser/misc/userscripts/rss b/.config/qutebrowser/misc/userscripts/rss
new file mode 100755
index 0000000..f8feebe
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/rss
@@ -0,0 +1,122 @@
+#!/bin/sh
+
+# Copyright 2016 Jan Verbeek (blyxxyz) <ring@openmailbox.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 <http://www.gnu.org/licenses/>.
+
+# This script keeps track of URLs in RSS feeds and opens new ones.
+# New feeds can be added with ':spawn -u /path/to/userscripts/rss add' or
+# ':spawn -u /path/to/userscripts/rss <url>'.
+# New items can be opened with ':spawn -u /path/to/userscripts/rss'.
+# The script doesn't really parse XML, and searches for things that look like
+# item links. It might open things that aren't real links, and it might miss
+# real links.
+
+config_dir="$HOME/.qute-rss"
+
+add_feed () {
+ touch "feeds"
+ if grep -Fq "$1" "feeds"; then
+ notice "$1 is saved already."
+ else
+ printf '%s\n' "$1" >> "feeds"
+ fi
+}
+
+# Show an error message and exit
+fail () {
+ echo "message-error '$*'" > "$QUTE_FIFO"
+ exit 1
+}
+
+# Get a sorted list of item URLs from a RSS feed
+get_items () {
+ $curl "$@" | grep "$text_only" -zo -e '<guid[^<>]*>[^<>]*</guid>' \
+ -e '<link[^<>]*>[^<>]*</link>' \
+ -e '<link[^<>]*href="[^"]*"' |
+ grep "$text_only" -o 'http[^<>"]*' | sort | uniq
+}
+
+# Show an info message
+notice () {
+ echo "message-info '$*'" > "$QUTE_FIFO"
+}
+
+# Update a database of a feed and open new URLs
+read_items () {
+ cd read_urls || return 1
+ feed_file="$(echo "$1" | tr -d /)"
+ feed_temp_file="$(mktemp "$feed_file.tmp.XXXXXXXXXX")"
+ feed_new_items="$(mktemp "$feed_file.new.XXXXXXXXXX")"
+ get_items "$1" > "$feed_temp_file"
+ if [ ! -s "$feed_temp_file" ]; then
+ notice "No items found for $1."
+ rm "$feed_temp_file" "$feed_new_items"
+ elif [ ! -f "$feed_file" ]; then
+ notice "$1 is a new feed. All items will be marked as read."
+ mv "$feed_temp_file" "$feed_file"
+ rm "$feed_new_items"
+ else
+ sort -o "$feed_file" "$feed_file"
+ comm -2 -3 "$feed_temp_file" "$feed_file" | tee "$feed_new_items"
+ cat "$feed_new_items" >> "$feed_file"
+ sort -o "$feed_file" "$feed_file"
+ rm "$feed_temp_file" "$feed_new_items"
+ fi | while read -r item; do
+ echo "open -t $item" > "$QUTE_FIFO"
+ done
+}
+
+if [ ! -d "$config_dir/read_urls" ]; then
+ notice "Creating configuration directory."
+ mkdir -p "$config_dir/read_urls"
+fi
+
+cd "$config_dir" || exit 1
+
+if [ $# != 0 ]; then
+ for arg in "$@"; do
+ if [ "$arg" = "add" ]; then
+ add_feed "$QUTE_URL"
+ else
+ add_feed "$arg"
+ fi
+ done
+ exit
+fi
+
+if [ ! -f "feeds" ]; then
+ fail "Add feeds by running ':spawn -u rss add' or ':spawn -u rss <url>'."
+fi
+
+if curl --version >&-; then
+ curl="curl -sL"
+elif wget --version >&-; then
+ curl="wget -qO -"
+else
+ fail "Either curl or wget is needed to run this script."
+fi
+
+# Detect GNU grep so we can force it to treat everything as text
+if < /dev/null grep --help 2>&1 | grep -q -- -a; then
+ text_only="-a"
+fi
+
+while read -r feed_url; do
+ read_items "$feed_url" &
+done < "$config_dir/feeds"
+
+wait
diff --git a/.config/qutebrowser/misc/userscripts/taskadd b/.config/qutebrowser/misc/userscripts/taskadd
new file mode 100755
index 0000000..36e1c2c
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/taskadd
@@ -0,0 +1,34 @@
+#!/usr/bin/env bash
+#
+# Behavior:
+# Userscript for qutebrowser which adds a task to taskwarrior.
+# If run as a command (:spawn --userscript taskadd), it creates a new task
+# with the description equal to the current page title and annotates it with
+# the current page url. Additional arguments are passed along so you can add
+# mods to the task (e.g. priority, due date, tags).
+#
+# Example:
+# :spawn --userscript taskadd due:eod pri:H
+#
+# To enable passing along extra args, I suggest using a mapping like:
+# :bind <somekey> set-cmd-text -s :spawn --userscript taskadd
+#
+# If run from hint mode, it uses the selected hint text as the description
+# and the selected hint url as the annotation.
+#
+# Ryan Roden-Corrent (rcorre), 2016
+# Any feedback is welcome!
+#
+# For more info on Taskwarrior, see http://taskwarrior.org/
+
+# use either the current page title or the hint text as the task description
+[[ $QUTE_MODE == 'hints' ]] && title=$QUTE_SELECTED_TEXT || title=$QUTE_TITLE
+
+# try to add the task and grab the output
+if msg="$(task add "$title" "$*" 2>&1)"; then
+ # annotate the new task with the url, send the output back to the browser
+ task +LATEST annotate "$QUTE_URL"
+ echo "message-info '$(echo "$msg" | head -n 1)'" >> "$QUTE_FIFO"
+else
+ echo "message-error '$(echo "$msg" | head -n 1)'" >> "$QUTE_FIFO"
+fi
diff --git a/.config/qutebrowser/misc/userscripts/tor_identity b/.config/qutebrowser/misc/userscripts/tor_identity
new file mode 100755
index 0000000..93b6d41
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/tor_identity
@@ -0,0 +1,52 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+
+# Copyright 2018 jnphilipp <mail@jnphilipp.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 <http://www.gnu.org/licenses/>.
+
+# Change your tor identity.
+#
+# Set a hotkey to launch this script, then:
+# :bind ti spawn --userscript tor_identity PASSWORD
+#
+# Use the hotkey to change your tor identity, press 'ti' to change it.
+# https://stem.torproject.org/faq.html#how-do-i-request-a-new-identity-from-tor
+#
+
+import os
+import sys
+
+try:
+ from stem import Signal
+ from stem.control import Controller
+except ImportError:
+ if os.getenv('QUTE_FIFO'):
+ with open(os.environ['QUTE_FIFO'], 'w') as f:
+ f.write('message-error "Failed to import stem."')
+ else:
+ print('Failed to import stem.')
+
+
+password = sys.argv[1]
+with Controller.from_port(port=9051) as controller:
+ controller.authenticate(password)
+ controller.signal(Signal.NEWNYM)
+ if os.getenv('QUTE_FIFO'):
+ with open(os.environ['QUTE_FIFO'], 'w') as f:
+ f.write('message-info "Tor identity changed."')
+ else:
+ print('Tor identity changed.')
diff --git a/.config/qutebrowser/misc/userscripts/view_in_mpv b/.config/qutebrowser/misc/userscripts/view_in_mpv
new file mode 100755
index 0000000..16603bd
--- /dev/null
+++ b/.config/qutebrowser/misc/userscripts/view_in_mpv
@@ -0,0 +1,143 @@
+#!/usr/bin/env bash
+#
+# Behavior:
+# Userscript for qutebrowser which views the current web page in mpv using
+# sensible mpv-flags. While viewing the page in MPV, all <video>, <embed>,
+# and <object> tags in the original page are temporarily removed. Clicking on
+# such a removed video restores the respective video.
+#
+# In order to use this script, just start it using `spawn --userscript` from
+# qutebrowser. I recommend using an alias, e.g. put this in the
+# [alias]-section of qutebrowser.conf:
+#
+# mpv = spawn --userscript /path/to/view_in_mpv
+#
+# Background:
+# Most of my machines are too slow to play youtube videos using html5, but
+# they work fine in mpv (and mpv has further advantages like video scaling,
+# etc). Of course, I don't want the video to be played (or even to be
+# downloaded) twice — in MPV and in qwebkit. So I often close the tab after
+# opening it in mpv. However, I actually want to keep the rest of the page
+# (comments and video suggestions), i.e. only the videos should disappear
+# when mpv is started. And that's precisely what the present script does.
+#
+# Thorsten Wißmann, 2015 (thorsten` on freenode)
+# Any feedback is welcome!
+
+set -e
+
+if [ -z "$QUTE_FIFO" ] ; then
+ cat 1>&2 <<EOF
+Error: $0 can not be run as a standalone script.
+
+It is a qutebrowser userscript. In order to use it, call it using
+'spawn --userscript' as described in qute://help/userscripts.html
+EOF
+ exit 1
+fi
+
+msg() {
+ local cmd="$1"
+ shift
+ local msg="$*"
+ if [ -z "$QUTE_FIFO" ] ; then
+ echo "$cmd: $msg" >&2
+ else
+ echo "message-$cmd '${msg//\'/\\\'}'" >> "$QUTE_FIFO"
+ fi
+}
+
+MPV_COMMAND=${MPV_COMMAND:-mpv}
+# Warning: spaces in single flags are not supported
+MPV_FLAGS=${MPV_FLAGS:- --force-window --no-terminal --keep-open=yes --ytdl}
+IFS=" " read -r -a video_command <<< "$MPV_COMMAND $MPV_FLAGS"
+
+js() {
+cat <<EOF
+
+ function descendantOfTagName(child, ancestorTagName) {
+ // tells whether child has some (proper) ancestor
+ // with the tag name ancestorTagName
+ while (child.parentNode != null) {
+ child = child.parentNode;
+ if (typeof child.tagName === 'undefined') break;
+ if (child.tagName.toUpperCase() == ancestorTagName.toUpperCase()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ var App = {};
+
+ var all_videos = [];
+ all_videos.push.apply(all_videos, document.getElementsByTagName("video"));
+ all_videos.push.apply(all_videos, document.getElementsByTagName("object"));
+ all_videos.push.apply(all_videos, document.getElementsByTagName("embed"));
+ App.backup_videos = Array();
+ App.all_replacements = Array();
+ for (i = 0; i < all_videos.length; i++) {
+ var video = all_videos[i];
+ if (descendantOfTagName(video, "object")) {
+ // skip tags that are contained in an object, because we hide
+ // the object anyway.
+ continue;
+ }
+ var replacement = document.createElement("div");
+ replacement.innerHTML = "
+ <p style=\\"margin-bottom: 0.5em\\">
+ Opening page with:
+ <span style=\\"font-family: monospace;\\">${video_command[*]}</span>
+ </p>
+ <p>
+ In order to restore this particular video
+ <a style=\\"font-weight: bold;
+ color: white;
+ background: transparent;
+ \\"
+ onClick=\\"restore_video(this, " + i + ");\\"
+ href=\\"javascript: restore_video(this, " + i + ")\\"
+ >click here</a>.
+ </p>
+ ";
+ replacement.style.position = "relative";
+ replacement.style.zIndex = "100003000000";
+ replacement.style.fontSize = "1rem";
+ replacement.style.textAlign = "center";
+ replacement.style.verticalAlign = "middle";
+ replacement.style.height = "100%";
+ replacement.style.background = "#101010";
+ replacement.style.color = "white";
+ replacement.style.border = "4px dashed #545454";
+ replacement.style.padding = "2em";
+ replacement.style.margin = "auto";
+ App.all_replacements[i] = replacement;
+ App.backup_videos[i] = video;
+ video.parentNode.replaceChild(replacement, video);
+ }
+
+ function restore_video(obj, index) {
+ obj = App.all_replacements[index];
+ video = App.backup_videos[index];
+ console.log(video);
+ obj.parentNode.replaceChild(video, obj);
+ }
+
+ /** force repainting the video, thanks to:
+ * http://martinwolf.org/2014/06/10/force-repaint-of-an-element-with-javascript/
+ */
+ var siteHeader = document.getElementById('header');
+ siteHeader.style.display='none';
+ siteHeader.offsetHeight; // no need to store this anywhere, the reference is enough
+ siteHeader.style.display='block';
+
+EOF
+}
+
+printjs() {
+ js | sed 's,//.*$,,' | tr '\n' ' '
+}
+echo "jseval -q $(printjs)" >> "$QUTE_FIFO"
+
+msg info "Opening $QUTE_URL with mpv"
+"${video_command[@]}" "$@" "$QUTE_URL"
diff --git a/.config/qutebrowser/nord-qutebrowser.py b/.config/qutebrowser/nord-qutebrowser.py
new file mode 100644
index 0000000..9dd9064
--- /dev/null
+++ b/.config/qutebrowser/nord-qutebrowser.py
@@ -0,0 +1,336 @@
+nord = {
+ # Polar Night
+ 'nord0': '#2e3440',
+ 'nord1': '#3b4252',
+ 'nord2': '#434c5e',
+ 'nord3': '#4c566a',
+ # Snow Storm
+ 'nord4': '#d8dee9',
+ 'nord5': '#e5e9f0',
+ 'nord6': '#eceff4',
+ # Frost
+ 'nord7': '#8fbcbb',
+ 'nord8': '#88c0d0',
+ 'nord9': '#81a1c1',
+ 'nord10': '#5e81ac',
+ # Aurora
+ 'nord11': '#bf616a',
+ 'nord12': '#d08770',
+ 'nord13': '#ebcb8b',
+ 'nord14': '#a3be8c',
+ 'nord15': '#b48ead',
+}
+
+## Background color of the completion widget category headers.
+## Type: QssColor
+c.colors.completion.category.bg = nord['nord0']
+
+## Bottom border color of the completion widget category headers.
+## Type: QssColor
+c.colors.completion.category.border.bottom = nord['nord0']
+
+## Top border color of the completion widget category headers.
+## Type: QssColor
+c.colors.completion.category.border.top = nord['nord0']
+
+## Foreground color of completion widget category headers.
+## Type: QtColor
+c.colors.completion.category.fg = nord['nord5']
+
+## Background color of the completion widget for even rows.
+## Type: QssColor
+c.colors.completion.even.bg = nord['nord1']
+
+## Background color of the completion widget for odd rows.
+## Type: QssColor
+c.colors.completion.odd.bg = nord['nord1']
+
+## Text color of the completion widget.
+## Type: QtColor
+c.colors.completion.fg = nord['nord4']
+
+## Background color of the selected completion item.
+## Type: QssColor
+c.colors.completion.item.selected.bg = nord['nord3']
+
+## Bottom border color of the selected completion item.
+## Type: QssColor
+c.colors.completion.item.selected.border.bottom = nord['nord3']
+
+## Top border color of the completion widget category headers.
+## Type: QssColor
+c.colors.completion.item.selected.border.top = nord['nord3']
+
+## Foreground color of the selected completion item.
+## Type: QtColor
+c.colors.completion.item.selected.fg = nord['nord6']
+
+## Foreground color of the matched text in the completion.
+## Type: QssColor
+c.colors.completion.match.fg = nord['nord13']
+
+## Color of the scrollbar in completion view
+## Type: QssColor
+c.colors.completion.scrollbar.bg = nord['nord1']
+
+## Color of the scrollbar handle in completion view.
+## Type: QssColor
+c.colors.completion.scrollbar.fg = nord['nord5']
+
+## Background color for the download bar.
+## Type: QssColor
+c.colors.downloads.bar.bg = nord['nord0']
+
+## Background color for downloads with errors.
+## Type: QtColor
+c.colors.downloads.error.bg = nord['nord11']
+
+## Foreground color for downloads with errors.
+## Type: QtColor
+c.colors.downloads.error.fg = nord['nord5']
+
+## Color gradient stop for download backgrounds.
+## Type: QtColor
+c.colors.downloads.stop.bg = nord['nord15']
+
+## Color gradient interpolation system for download backgrounds.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+c.colors.downloads.system.bg = 'none'
+
+## Background color for hints. Note that you can use a `rgba(...)` value
+## for transparency.
+## Type: QssColor
+c.colors.hints.bg = nord['nord13']
+
+## Font color for hints.
+## Type: QssColor
+c.colors.hints.fg = nord['nord0']
+
+## Font color for the matched part of hints.
+## Type: QssColor
+c.colors.hints.match.fg = nord['nord10']
+
+## Background color of the keyhint widget.
+## Type: QssColor
+c.colors.keyhint.bg = nord['nord1']
+
+## Text color for the keyhint widget.
+## Type: QssColor
+c.colors.keyhint.fg = nord['nord5']
+
+## Highlight color for keys to complete the current keychain.
+## Type: QssColor
+c.colors.keyhint.suffix.fg = nord['nord13']
+
+## Background color of an error message.
+## Type: QssColor
+c.colors.messages.error.bg = nord['nord11']
+
+## Border color of an error message.
+## Type: QssColor
+c.colors.messages.error.border = nord['nord11']
+
+## Foreground color of an error message.
+## Type: QssColor
+c.colors.messages.error.fg = nord['nord5']
+
+## Background color of an info message.
+## Type: QssColor
+c.colors.messages.info.bg = nord['nord8']
+
+## Border color of an info message.
+## Type: QssColor
+c.colors.messages.info.border = nord['nord8']
+
+## Foreground color an info message.
+## Type: QssColor
+c.colors.messages.info.fg = nord['nord5']
+
+## Background color of a warning message.
+## Type: QssColor
+c.colors.messages.warning.bg = nord['nord12']
+
+## Border color of a warning message.
+## Type: QssColor
+c.colors.messages.warning.border = nord['nord12']
+
+## Foreground color a warning message.
+## Type: QssColor
+c.colors.messages.warning.fg = nord['nord5']
+
+## Background color for prompts.
+## Type: QssColor
+c.colors.prompts.bg = nord['nord2']
+
+# ## Border used around UI elements in prompts.
+# ## Type: String
+c.colors.prompts.border = '1px solid ' + nord['nord0']
+
+## Foreground color for prompts.
+## Type: QssColor
+c.colors.prompts.fg = nord['nord5']
+
+## Background color for the selected item in filename prompts.
+## Type: QssColor
+c.colors.prompts.selected.bg = nord['nord3']
+
+## Background color of the statusbar in caret mode.
+## Type: QssColor
+c.colors.statusbar.caret.bg = nord['nord15']
+
+## Foreground color of the statusbar in caret mode.
+## Type: QssColor
+c.colors.statusbar.caret.fg = nord['nord5']
+
+## Background color of the statusbar in caret mode with a selection.
+## Type: QssColor
+c.colors.statusbar.caret.selection.bg = nord['nord15']
+
+## Foreground color of the statusbar in caret mode with a selection.
+## Type: QssColor
+c.colors.statusbar.caret.selection.fg = nord['nord5']
+
+## Background color of the statusbar in command mode.
+## Type: QssColor
+c.colors.statusbar.command.bg = nord['nord2']
+
+## Foreground color of the statusbar in command mode.
+## Type: QssColor
+c.colors.statusbar.command.fg = nord['nord5']
+
+## Background color of the statusbar in private browsing + command mode.
+## Type: QssColor
+c.colors.statusbar.command.private.bg = nord['nord2']
+
+## Foreground color of the statusbar in private browsing + command mode.
+## Type: QssColor
+c.colors.statusbar.command.private.fg = nord['nord5']
+
+## Background color of the statusbar in insert mode.
+## Type: QssColor
+c.colors.statusbar.insert.bg = nord['nord14']
+
+## Foreground color of the statusbar in insert mode.
+## Type: QssColor
+c.colors.statusbar.insert.fg = nord['nord1']
+
+## Background color of the statusbar.
+## Type: QssColor
+c.colors.statusbar.normal.bg = nord['nord0']
+
+## Foreground color of the statusbar.
+## Type: QssColor
+c.colors.statusbar.normal.fg = nord['nord5']
+
+## Background color of the statusbar in passthrough mode.
+## Type: QssColor
+c.colors.statusbar.passthrough.bg = nord['nord10']
+
+## Foreground color of the statusbar in passthrough mode.
+## Type: QssColor
+c.colors.statusbar.passthrough.fg = nord['nord5']
+
+## Background color of the statusbar in private browsing mode.
+## Type: QssColor
+c.colors.statusbar.private.bg = nord['nord3']
+
+## Foreground color of the statusbar in private browsing mode.
+## Type: QssColor
+c.colors.statusbar.private.fg = nord['nord5']
+
+## Background color of the progress bar.
+## Type: QssColor
+c.colors.statusbar.progress.bg = nord['nord5']
+
+## Foreground color of the URL in the statusbar on error.
+## Type: QssColor
+c.colors.statusbar.url.error.fg = nord['nord11']
+
+## Default foreground color of the URL in the statusbar.
+## Type: QssColor
+c.colors.statusbar.url.fg = nord['nord5']
+
+## Foreground color of the URL in the statusbar for hovered links.
+## Type: QssColor
+c.colors.statusbar.url.hover.fg = nord['nord8']
+
+## Foreground color of the URL in the statusbar on successful load
+## (http).
+## Type: QssColor
+c.colors.statusbar.url.success.http.fg = nord['nord5']
+
+## Foreground color of the URL in the statusbar on successful load
+## (https).
+## Type: QssColor
+c.colors.statusbar.url.success.https.fg = nord['nord14']
+
+## Foreground color of the URL in the statusbar when there's a warning.
+## Type: QssColor
+c.colors.statusbar.url.warn.fg = nord['nord12']
+
+## Background color of the tab bar.
+## Type: QtColor
+c.colors.tabs.bar.bg = nord['nord3']
+
+## Background color of unselected even tabs.
+## Type: QtColor
+c.colors.tabs.even.bg = nord['nord3']
+
+## Foreground color of unselected even tabs.
+## Type: QtColor
+c.colors.tabs.even.fg = nord['nord5']
+
+## Color for the tab indicator on errors.
+## Type: QtColor
+c.colors.tabs.indicator.error = nord['nord11']
+
+## Color gradient start for the tab indicator.
+## Type: QtColor
+# c.colors.tabs.indicator.start = nord['violet']
+
+## Color gradient end for the tab indicator.
+## Type: QtColor
+# c.colors.tabs.indicator.stop = nord['orange']
+
+## Color gradient interpolation system for the tab indicator.
+## Type: ColorSystem
+## Valid values:
+## - rgb: Interpolate in the RGB color system.
+## - hsv: Interpolate in the HSV color system.
+## - hsl: Interpolate in the HSL color system.
+## - none: Don't show a gradient.
+c.colors.tabs.indicator.system = 'none'
+
+## Background color of unselected odd tabs.
+## Type: QtColor
+c.colors.tabs.odd.bg = nord['nord3']
+
+## Foreground color of unselected odd tabs.
+## Type: QtColor
+c.colors.tabs.odd.fg = nord['nord5']
+
+# ## Background color of selected even tabs.
+# ## Type: QtColor
+c.colors.tabs.selected.even.bg = nord['nord0']
+
+# ## Foreground color of selected even tabs.
+# ## Type: QtColor
+c.colors.tabs.selected.even.fg = nord['nord5']
+
+# ## Background color of selected odd tabs.
+# ## Type: QtColor
+c.colors.tabs.selected.odd.bg = nord['nord0']
+
+# ## Foreground color of selected odd tabs.
+# ## Type: QtColor
+c.colors.tabs.selected.odd.fg = nord['nord5']
+
+## Background color for webpages if unset (or empty to use the theme's
+## color)
+## Type: QtColor
+# c.colors.webpage.bg = 'white'
diff --git a/.config/qutebrowser/qsettings/QtProject.conf b/.config/qutebrowser/qsettings/QtProject.conf
new file mode 100644
index 0000000..8777b99
--- /dev/null
+++ b/.config/qutebrowser/qsettings/QtProject.conf
@@ -0,0 +1,8 @@
+[FileDialog]
+history=file:///home/archlinux/vgg, file:///home/archlinux/vgg/Downloads, file:///home/archlinux/vgg/Sandbox/Python/PlasmaPySandbox, file:///home/archlinux/vgg/Documents/Astro/UoH/Teaching/PlasmaPhysics/Lecture4, file:///home/archlinux/vgg/Requisition/LizzieNoakes
+lastVisited=file:///home/archlinux/vgg/Requisition/LizzieNoakes
+qtVersion=5.13.2
+shortcuts=file:, file:///home/archlinux/vgg
+sidebarWidth=98
+treeViewHeader=@ByteArray(\0\0\0\xff\0\0\0\0\0\0\0\x1\0\0\0\0\0\0\0\0\x1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x1\xec\0\0\0\x4\x1\x1\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x64\xff\xff\xff\xff\0\0\0\x81\0\0\0\0\0\0\0\x4\0\0\0\xff\0\0\0\x1\0\0\0\0\0\0\0?\0\0\0\x1\0\0\0\0\0\0\0@\0\0\0\x1\0\0\0\0\0\0\0n\0\0\0\x1\0\0\0\0\0\0\x3\xe8\0\xff\xff\xff\xff)
+viewMode=Detail
diff --git a/.config/qutebrowser/quickmarks b/.config/qutebrowser/quickmarks
new file mode 100755
index 0000000..61cd7db
--- /dev/null
+++ b/.config/qutebrowser/quickmarks
@@ -0,0 +1 @@
+bookmark file://${HOME}/bookmarks.html
diff --git a/.config/qutebrowser/qutebrowser.conf b/.config/qutebrowser/qutebrowser.conf
new file mode 100755
index 0000000..3fc85a8
--- /dev/null
+++ b/.config/qutebrowser/qutebrowser.conf
@@ -0,0 +1,1612 @@
+# vim: ft=dosini
+
+# Configfile for qutebrowser.
+#
+# This configfile is parsed by python's configparser in extended
+# interpolation mode. The format is very INI-like, so there are
+# categories like [general] with "key = value"-pairs.
+#
+# Note that you shouldn't add your own comments, as this file is
+# regenerated every time the config is saved.
+#
+# Interpolation looks like ${value} or ${section:value} and will be
+# replaced by the respective value.
+#
+# Some settings will expand environment variables. Note that, since
+# interpolation is run first, you will need to escape the $ char as
+# described below.
+#
+# This is the default config, so if you want to remove anything from
+# here (as opposed to change/add), for example a key binding, set it to
+# an empty value.
+#
+# You will need to escape the following values:
+# - # at the start of the line (at the first position of the key) (\#)
+# - $ in a value ($$)
+
+
+# General/miscellaneous options.
+[general]
+
+# ignore-case (IgnoreCase):
+# Whether to find text on a page case-insensitively.
+# true: Search case-insensitively
+# false: Search case-sensitively
+# smart: Search case-sensitively if there are capital chars
+# Default: smart
+ignore-case = smart
+
+# startpage (List of String):
+# The default page(s) to open at the start, separated by commas.
+# Default: https://start.duckduckgo.com
+startpage = https://start.duckduckgo.com
+
+# yank-ignored-url-parameters (List of String):
+# The URL parameters to strip with :yank url, separated by commas.
+# Default:
+# ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content
+yank-ignored-url-parameters = ref,utm_source,utm_medium,utm_campaign,utm_term,utm_content
+
+# default-open-dispatcher (String):
+# The default program used to open downloads. Set to an empty string
+# to use the default internal handler.
+# Any {} in the string will be expanded to the filename, else the
+# filename will be appended.
+# Default:
+default-open-dispatcher =
+
+# default-page (FuzzyUrl):
+# The page to open if :open -t/-b/-w is used without URL. Use
+# `about:blank` for a blank page.
+# Default: ${startpage}
+default-page = ${startpage}
+
+# auto-search (AutoSearch):
+# Whether to start a search when something else than a URL is
+# entered.
+# naive: Use simple/naive check.
+# dns: Use DNS requests (might be slow!).
+# false: Never search automatically.
+# Default: naive
+auto-search = naive
+
+# auto-save-config (Bool):
+# Whether to save the config automatically on quit.
+# Valid values: true, false
+# Default: true
+auto-save-config = true
+
+# auto-save-interval (Int):
+# How often (in milliseconds) to auto-save config/cookies/etc.
+# Default: 15000
+auto-save-interval = 15000
+
+# editor (ShellCommand):
+# The editor (and arguments) to use for the `open-editor` command.
+# The arguments get split like in a shell, so you can use `"` or `'`
+# to quote them.
+# `{}` gets replaced by the filename of the file to be edited.
+# Default: gvim -f "{}"
+editor = gvim -f "{}"
+
+# editor-encoding (Encoding):
+# Encoding to use for editor.
+# Default: utf-8
+editor-encoding = utf-8
+
+# private-browsing (Bool):
+# Open new windows in private browsing mode which does not record
+# visited pages.
+# Valid values: true, false
+# Default: false
+private-browsing = true
+
+# developer-extras (Bool):
+# Enable extra tools for Web developers.
+# This needs to be enabled for `:inspector` to work and also adds an
+# _Inspect_ entry to the context menu. For QtWebEngine, see
+# 'qutebrowser --help' instead.
+# Valid values: true, false
+# Default: false
+developer-extras = true
+
+# print-element-backgrounds (Bool):
+# Whether the background color and images are also drawn when the
+# page is printed.
+# This setting only works with Qt 5.8 or newer when using the
+# QtWebEngine backend.
+# Valid values: true, false
+# Default: true
+print-element-backgrounds = true
+
+# xss-auditing (Bool):
+# Whether load requests should be monitored for cross-site scripting
+# attempts.
+# Suspicious scripts will be blocked and reported in the inspector's
+# JavaScript console. Enabling this feature might have an impact on
+# performance.
+# Valid values: true, false
+# Default: false
+xss-auditing = false
+
+# default-encoding (String):
+# Default encoding to use for websites.
+# The encoding must be a string describing an encoding such as
+# _utf-8_, _iso-8859-1_, etc.
+# Default: iso-8859-1
+default-encoding = _utf-8_
+
+# new-instance-open-target (String):
+# How to open links in an existing instance if a new one is
+# launched.
+# tab: Open a new tab in the existing window and activate the
+# window.
+# tab-bg: Open a new background tab in the existing window and
+# activate the window.
+# tab-silent: Open a new tab in the existing window without
+# activating the window.
+# tab-bg-silent: Open a new background tab in the existing
+# window without activating the window.
+# window: Open in a new window.
+# Default: tab
+new-instance-open-target = tab
+
+# new-instance-open-target.window (String):
+# Which window to choose when opening links as new tabs.
+# first-opened: Open new tabs in the first (oldest) opened
+# window.
+# last-opened: Open new tabs in the last (newest) opened window.
+# last-focused: Open new tabs in the most recently focused
+# window.
+# last-visible: Open new tabs in the most recently visible
+# window.
+# Default: last-focused
+new-instance-open-target.window = last-focused
+
+# log-javascript-console (String):
+# How to log javascript console messages.
+# none: Don't log messages.
+# debug: Log messages with debug level.
+# info: Log messages with info level.
+# Default: debug
+log-javascript-console = debug
+
+# save-session (Bool):
+# Whether to always save the open pages.
+# Valid values: true, false
+# Default: false
+save-session = true
+
+# session-default-name (SessionName):
+# The name of the session to save by default, or empty for the last
+# loaded session.
+# Default:
+session-default-name =
+
+# url-incdec-segments (FlagList):
+# The URL segments where `:navigate increment/decrement` will search
+# for a number.
+# Valid values: host, path, query, anchor
+# Default: path,query
+url-incdec-segments = path,query
+
+
+# General options related to the user interface.
+[ui]
+
+# history-session-interval (Int):
+# The maximum time in minutes between two history items for them to
+# be considered being from the same session. Use -1 to disable
+# separation.
+# Default: 30
+history-session-interval = 30
+
+# zoom-levels (List of Perc):
+# The available zoom levels, separated by commas.
+# Default:
+# 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500%
+zoom-levels = 25%,33%,50%,67%,75%,90%,100%,110%,125%,150%,175%,200%,250%,300%,400%,500%
+
+# default-zoom (Perc):
+# The default zoom level.
+# Default: 100%
+default-zoom = 100%
+
+# downloads-position (VerticalPosition):
+# Where to show the downloaded files.
+# Valid values: top, bottom
+# Default: top
+downloads-position = top
+
+# status-position (VerticalPosition):
+# The position of the status bar.
+# Valid values: top, bottom
+# Default: bottom
+status-position = bottom
+
+# message-timeout (Int):
+# Time (in ms) to show messages in the statusbar for.
+# Set to 0 to never clear messages.
+# Default: 2000
+message-timeout = 2000
+
+# message-unfocused (Bool):
+# Whether to show messages in unfocused windows.
+# Valid values: true, false
+# Default: false
+message-unfocused = false
+
+# confirm-quit (ConfirmQuit):
+# Whether to confirm quitting the application.
+# always: Always show a confirmation.
+# multiple-tabs: Show a confirmation if multiple tabs are
+# opened.
+# downloads: Show a confirmation if downloads are running
+# never: Never show a confirmation.
+# Default: never
+confirm-quit = never
+
+# zoom-text-only (Bool):
+# Whether the zoom factor on a frame applies only to the text or to
+# all content.
+# Valid values: true, false
+# Default: false
+zoom-text-only = false
+
+# frame-flattening (Bool):
+# Whether to expand each subframe to its contents.
+# This will flatten all the frames to become one scrollable page.
+# Valid values: true, false
+# Default: false
+frame-flattening = false
+
+# user-stylesheet (File):
+# User stylesheet to use (absolute filename or filename relative to
+# the config directory). Will expand environment variables.
+# Default:
+user-stylesheet =
+
+# hide-scrollbar (Bool):
+# Hide the main scrollbar.
+# Valid values: true, false
+# Default: true
+hide-scrollbar = true
+
+# smooth-scrolling (Bool):
+# Whether to enable smooth scrolling for web pages. Note smooth
+# scrolling does not work with the :scroll-px command.
+# Valid values: true, false
+# Default: false
+smooth-scrolling = false
+
+# remove-finished-downloads (Int):
+# Number of milliseconds to wait before removing finished downloads.
+# Will not be removed if value is -1.
+# Default: -1
+remove-finished-downloads = -1
+
+# hide-statusbar (Bool):
+# Whether to hide the statusbar unless a message is shown.
+# Valid values: true, false
+# Default: false
+hide-statusbar = false
+
+# statusbar-padding (Padding):
+# Padding for statusbar (top, bottom, left, right).
+# Default: 1,1,0,0
+statusbar-padding = 1,1,0,0
+
+# window-title-format (FormatString):
+# The format to use for the window title. The following placeholders
+# are defined:
+# * `{perc}`: The percentage as a string like `[10%]`.
+# * `{perc_raw}`: The raw percentage, e.g. `10`
+# * `{title}`: The title of the current web page
+# * `{title_sep}`: The string ` - ` if a title is set, empty
+# otherwise.
+# * `{id}`: The internal window ID of this window.
+# * `{scroll_pos}`: The page scroll position.
+# * `{host}`: The host of the current web page.
+# * `{backend}`: Either 'webkit' or 'webengine'
+# * `{private}` : Indicates when private mode is enabled.
+# Default: {perc}{title}{title_sep}qutebrowser
+window-title-format = {perc}{title}{title_sep}qutebrowser
+
+# modal-js-dialog (Bool):
+# Use standard JavaScript modal dialog for alert() and confirm()
+# Valid values: true, false
+# Default: false
+modal-js-dialog = false
+
+# hide-wayland-decoration (Bool):
+# Hide the window decoration when using wayland (requires restart)
+# Valid values: true, false
+# Default: false
+hide-wayland-decoration = false
+
+# keyhint-blacklist (List of String):
+# Keychains that shouldn't be shown in the keyhint dialog
+# Globs are supported, so ';*' will blacklist all keychainsstarting
+# with ';'. Use '*' to disable keyhints
+# Default:
+keyhint-blacklist =
+
+# keyhint-delay (Int):
+# Time from pressing a key to seeing the keyhint dialog (ms)
+# Default: 500
+keyhint-delay = 500
+
+# prompt-radius (Int):
+# The rounding radius for the edges of prompts.
+# Default: 8
+prompt-radius = 8
+
+# prompt-filebrowser (Bool):
+# Show a filebrowser in upload/download prompts.
+# Valid values: true, false
+# Default: true
+prompt-filebrowser = true
+
+
+# Settings related to the network.
+[network]
+
+# do-not-track (Bool):
+# Value to send in the `DNT` header.
+# Valid values: true, false
+# Default: true
+do-not-track = true
+
+# accept-language (String):
+# Value to send in the `accept-language` header.
+# Default: en-US,en
+accept-language = en-US,en;q=0.5
+
+# referer-header (String):
+# Send the Referer header
+# always: Always send.
+# never: Never send; this is not recommended, as some sites may
+# break.
+# same-domain: Only send for the same domain. This will still
+# protect your privacy, but shouldn't break any sites.
+# Default: same-domain
+referer-header = same-domain
+
+# user-agent (UserAgent):
+# User agent to send. Empty to send the default.
+# Default:
+user-agent = Mozilla/5.0
+
+# proxy (Proxy):
+# The proxy to use.
+# In addition to the listed values, you can use a `socks://...` or
+# `http://...` URL.
+# system: Use the system wide proxy.
+# none: Don't use any proxy
+# Default: system
+proxy = system
+
+# proxy-dns-requests (Bool):
+# Whether to send DNS requests over the configured proxy.
+# Valid values: true, false
+# Default: true
+proxy-dns-requests = true
+
+# ssl-strict (BoolAsk):
+# Whether to validate SSL handshakes.
+# Valid values: true, false, ask
+# Default: ask
+ssl-strict = ask
+
+# dns-prefetch (Bool):
+# Whether to try to pre-fetch DNS entries to speed up browsing.
+# Valid values: true, false
+# Default: true
+dns-prefetch = true
+
+# custom-headers (HeaderDict):
+# Set custom headers for qutebrowser HTTP requests.
+# Default:
+custom-headers =
+
+# netrc-file (File):
+# Set location of a netrc-file for HTTP authentication. If empty,
+# ~/.netrc is used.
+# Default:
+netrc-file =
+
+
+# Options related to completion and command history.
+[completion]
+
+# show (String):
+# When to show the autocompletion window.
+# always: Whenever a completion is available.
+# auto: Whenever a completion is requested.
+# never: Never.
+# Default: always
+show = always
+
+# download-path-suggestion (String):
+# What to display in the download filename input.
+# path: Show only the download path.
+# filename: Show only download filename.
+# both: Show download path and filename.
+# Default: path
+download-path-suggestion = path
+
+# timestamp-format (TimestampTemplate):
+# How to format timestamps (e.g. for history)
+# Default: %Y-%m-%d
+timestamp-format = %Y-%m-%d
+
+# height (PercOrInt):
+# The height of the completion, in px or as percentage of the
+# window.
+# Default: 50%
+height = 50%
+
+# cmd-history-max-items (Int):
+# How many commands to save in the command history.
+# 0: no history / -1: unlimited
+# Default: 100
+cmd-history-max-items = 100
+
+# web-history-max-items (Int):
+# How many URLs to show in the web history.
+# 0: no history / -1: unlimited
+# Default: 1000
+web-history-max-items = 1000
+
+# quick-complete (Bool):
+# Whether to move on to the next part when there's only one possible
+# completion left.
+# Valid values: true, false
+# Default: true
+quick-complete = true
+
+# shrink (Bool):
+# Whether to shrink the completion to be smaller than the configured
+# size if there are no scrollbars.
+# Valid values: true, false
+# Default: false
+shrink = false
+
+# scrollbar-width (Int):
+# Width of the scrollbar in the completion window (in px).
+# Default: 12
+scrollbar-width = 12
+
+# scrollbar-padding (Int):
+# Padding of scrollbar handle in completion window (in px).
+# Default: 2
+scrollbar-padding = 2
+
+
+# Options related to input modes.
+[input]
+
+# timeout (Int):
+# Timeout (in milliseconds) for ambiguous key bindings.
+# If the current input forms both a complete match and a partial
+# match, the complete match will be executed after this time.
+# Default: 500
+timeout = 500
+
+# partial-timeout (Int):
+# Timeout (in milliseconds) for partially typed key bindings.
+# If the current input forms only partial matches, the keystring
+# will be cleared after this time.
+# Default: 5000
+partial-timeout = 5000
+
+# insert-mode-on-plugins (Bool):
+# Whether to switch to insert mode when clicking flash and other
+# plugins.
+# Valid values: true, false
+# Default: false
+insert-mode-on-plugins = false
+
+# auto-leave-insert-mode (Bool):
+# Whether to leave insert mode if a non-editable element is clicked.
+# Valid values: true, false
+# Default: true
+auto-leave-insert-mode = true
+
+# auto-insert-mode (Bool):
+# Whether to automatically enter insert mode if an editable element
+# is focused after page load.
+# Valid values: true, false
+# Default: false
+auto-insert-mode = false
+
+# forward-unbound-keys (String):
+# Whether to forward unbound keys to the webview in normal mode.
+# all: Forward all unbound keys.
+# auto: Forward unbound non-alphanumeric keys.
+# none: Don't forward any keys.
+# Default: auto
+forward-unbound-keys = auto
+
+# spatial-navigation (Bool):
+# Enables or disables the Spatial Navigation feature.
+# 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 a 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.
+# Valid values: true, false
+# Default: false
+spatial-navigation = false
+
+# links-included-in-focus-chain (Bool):
+# Whether hyperlinks should be included in the keyboard focus chain.
+# Valid values: true, false
+# Default: true
+links-included-in-focus-chain = true
+
+# rocker-gestures (Bool):
+# Whether to enable Opera-like mouse rocker gestures. This disables
+# the context menu.
+# Valid values: true, false
+# Default: false
+rocker-gestures = false
+
+# mouse-zoom-divider (Int):
+# How much to divide the mouse wheel movements to translate them
+# into zoom increments.
+# Default: 512
+mouse-zoom-divider = 512
+
+
+# Configuration of the tab bar.
+[tabs]
+
+# background-tabs (Bool):
+# Whether to open new tabs (middleclick/ctrl+click) in background.
+# Valid values: true, false
+# Default: false
+background-tabs = false
+
+# select-on-remove (SelectOnRemove):
+# Which tab to select when the focused tab is removed.
+# prev: Select the tab which came before the closed one (left in
+# horizontal, above in vertical).
+# next: Select the tab which came after the closed one (right in
+# horizontal, below in vertical).
+# last-used: Select the previously selected tab.
+# Default: next
+select-on-remove = next
+
+# new-tab-position (NewTabPosition):
+# How new tabs are positioned.
+# prev: Before the current tab.
+# next: After the current tab.
+# first: At the beginning.
+# last: At the end.
+# Default: next
+new-tab-position = next
+
+# new-tab-position-explicit (NewTabPosition):
+# How new tabs opened explicitly are positioned.
+# prev: Before the current tab.
+# next: After the current tab.
+# first: At the beginning.
+# last: At the end.
+# Default: last
+new-tab-position-explicit = last
+
+# last-close (String):
+# Behavior when the last tab is closed.
+# ignore: Don't do anything.
+# blank: Load a blank page.
+# startpage: Load the start page.
+# default-page: Load the default page.
+# close: Close the window.
+# Default: ignore
+last-close = ignore
+
+# show (String):
+# When to show the tab bar
+# always: Always show the tab bar.
+# never: Always hide the tab bar.
+# multiple: Hide the tab bar if only one tab is open.
+# switching: Show the tab bar when switching tabs.
+# Default: always
+show = always
+
+# show-switching-delay (Int):
+# Time to show the tab bar before hiding it when tabs->show is set
+# to 'switching'.
+# Default: 800
+show-switching-delay = 800
+
+# wrap (Bool):
+# Whether to wrap when changing tabs.
+# Valid values: true, false
+# Default: true
+wrap = true
+
+# movable (Bool):
+# Whether tabs should be movable.
+# Valid values: true, false
+# Default: true
+movable = true
+
+# close-mouse-button (String):
+# On which mouse button to close tabs.
+# right: Close tabs on right-click.
+# middle: Close tabs on middle-click.
+# none: Don't close tabs using the mouse.
+# Default: middle
+close-mouse-button = middle
+
+# position (Position):
+# The position of the tab bar.
+# Valid values: top, bottom, left, right
+# Default: top
+position = top
+
+# show-favicons (Bool):
+# Whether to show favicons in the tab bar.
+# Valid values: true, false
+# Default: true
+show-favicons = true
+
+# favicon-scale (Float):
+# Scale for favicons in the tab bar. The tab size is unchanged, so
+# big favicons also require extra `tabs->padding`.
+# Default: 1.0
+favicon-scale = 1.0
+
+# width (PercOrInt):
+# The width of the tab bar if it's vertical, in px or as percentage
+# of the window.
+# Default: 20%
+width = 20%
+
+# pinned-width (Int):
+# The width for pinned tabs with a horizontal tabbar, in px.
+# Default: 43
+pinned-width = 43
+
+# indicator-width (Int):
+# Width of the progress indicator (0 to disable).
+# Default: 3
+indicator-width = 3
+
+# tabs-are-windows (Bool):
+# Whether to open windows instead of tabs.
+# Valid values: true, false
+# Default: false
+tabs-are-windows = false
+
+# title-format (FormatString):
+# The format to use for the tab title. The following placeholders
+# are defined:
+# * `{perc}`: The percentage as a string like `[10%]`.
+# * `{perc_raw}`: The raw percentage, e.g. `10`
+# * `{title}`: The title of the current web page
+# * `{title_sep}`: The string ` - ` if a title is set, empty
+# otherwise.
+# * `{index}`: The index of this tab.
+# * `{id}`: The internal tab ID of this tab.
+# * `{scroll_pos}`: The page scroll position.
+# * `{host}`: The host of the current web page.
+# * `{backend}`: Either 'webkit' or 'webengine'
+# * `{private}` : Indicates when private mode is enabled.
+# Default: {index}: {title}
+title-format = {index}: {title}
+
+# title-format-pinned (FormatString):
+# The format to use for the tab title for pinned tabs. The same
+# placeholders like for title-format are defined.
+# Default: {index}
+title-format-pinned = {index}
+
+# title-alignment (TextAlignment):
+# Alignment of the text inside of tabs
+# Valid values: left, right, center
+# Default: left
+title-alignment = left
+
+# mousewheel-tab-switching (Bool):
+# Switch between tabs using the mouse wheel.
+# Valid values: true, false
+# Default: true
+mousewheel-tab-switching = true
+
+# padding (Padding):
+# Padding for tabs (top, bottom, left, right).
+# Default: 0,0,5,5
+padding = 0,0,5,5
+
+# indicator-padding (Padding):
+# Padding for indicators (top, bottom, left, right).
+# Default: 2,2,0,4
+indicator-padding = 2,2,0,4
+
+
+# Settings related to cache and storage.
+[storage]
+
+# download-directory (Directory):
+# The directory to save downloads to. An empty value selects a
+# sensible os-specific default. Will expand environment variables.
+# Default:
+download-directory =
+
+# prompt-download-directory (Bool):
+# Whether to prompt the user for the download location.
+# If set to false, 'download-directory' will be used.
+# Valid values: true, false
+# Default: true
+prompt-download-directory = true
+
+# remember-download-directory (Bool):
+# Whether to remember the last used download directory.
+# Valid values: true, false
+# Default: true
+remember-download-directory = true
+
+# maximum-pages-in-cache (Int):
+# The maximum number of pages to hold in the global memory page
+# cache.
+# The Page Cache allows for a nicer user experience when navigating
+# forth or back to pages in the forward/back history, by pausing and
+# resuming up to _n_ pages.
+# For more information about the feature, please refer to:
+# http://webkit.org/blog/427/webkit-page-cache-i-the-basics/
+# Default: 0
+maximum-pages-in-cache = 0
+
+# offline-web-application-cache (Bool):
+# Whether support for the HTML 5 web application cache feature is
+# enabled.
+# 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.
+# The feature is described in details at:
+# http://dev.w3.org/html5/spec/Overview.html#appcache
+# Valid values: true, false
+# Default: true
+offline-web-application-cache = true
+
+# local-storage (Bool):
+# Whether support for HTML 5 local storage and Web SQL is enabled.
+# Valid values: true, false
+# Default: true
+local-storage = true
+
+# cache-size (Int):
+# Size of the HTTP network cache. Empty to use the default value.
+# Default:
+cache-size =
+
+
+# Loaded plugins/scripts and allowed actions.
+[content]
+
+# allow-images (Bool):
+# Whether images are automatically loaded in web pages.
+# Valid values: true, false
+# Default: true
+allow-images = true
+
+# allow-javascript (Bool):
+# Enables or disables the running of JavaScript programs.
+# Valid values: true, false
+# Default: true
+allow-javascript = true
+
+# allow-plugins (Bool):
+# Enables or disables plugins in Web pages.
+# Qt plugins with a mimetype such as "application/x-qt-plugin" are
+# not affected by this setting.
+# Valid values: true, false
+# Default: false
+allow-plugins = true
+
+# webgl (Bool):
+# Enables or disables WebGL.
+# Valid values: true, false
+# Default: true
+webgl = true
+
+# hyperlink-auditing (Bool):
+# Enable or disable hyperlink auditing (<a ping>).
+# Valid values: true, false
+# Default: false
+hyperlink-auditing = false
+
+# geolocation (BoolAsk):
+# Allow websites to request geolocations.
+# Valid values: true, false, ask
+# Default: ask
+geolocation = false
+
+# notifications (BoolAsk):
+# Allow websites to show notifications.
+# Valid values: true, false, ask
+# Default: ask
+notifications = ask
+
+# media-capture (BoolAsk):
+# Allow websites to record audio/video.
+# Valid values: true, false, ask
+# Default: ask
+media-capture = ask
+
+# javascript-can-open-windows-automatically (Bool):
+# Whether JavaScript programs can open new windows without user
+# interaction.
+# Valid values: true, false
+# Default: false
+javascript-can-open-windows-automatically = true
+
+# javascript-can-close-windows (Bool):
+# Whether JavaScript programs can close windows.
+# Valid values: true, false
+# Default: false
+javascript-can-close-windows = true
+
+# javascript-can-access-clipboard (Bool):
+# Whether JavaScript programs can read or write to the clipboard.
+# With QtWebEngine, writing the clipboard as response to a user
+# interaction is always allowed.
+# Valid values: true, false
+# Default: false
+javascript-can-access-clipboard = true
+
+# ignore-javascript-prompt (Bool):
+# Whether all javascript prompts should be ignored.
+# Valid values: true, false
+# Default: false
+ignore-javascript-prompt = true
+
+# ignore-javascript-alert (Bool):
+# Whether all javascript alerts should be ignored.
+# Valid values: true, false
+# Default: false
+ignore-javascript-alert = true
+
+# local-content-can-access-remote-urls (Bool):
+# Whether locally loaded documents are allowed to access remote
+# urls.
+# Valid values: true, false
+# Default: false
+local-content-can-access-remote-urls = false
+
+# local-content-can-access-file-urls (Bool):
+# Whether locally loaded documents are allowed to access other local
+# urls.
+# Valid values: true, false
+# Default: true
+local-content-can-access-file-urls = true
+
+# cookies-accept (String):
+# Control which cookies to accept.
+# all: Accept all cookies.
+# no-3rdparty: Accept cookies from the same origin only.
+# no-unknown-3rdparty: Accept cookies from the same origin only,
+# unless a cookie is already set for the domain.
+# never: Don't accept cookies at all.
+# Default: no-3rdparty
+cookies-accept = all
+
+# cookies-store (Bool):
+# Whether to store cookies. Note this option needs a restart with
+# QtWebEngine on Qt < 5.9.
+# Valid values: true, false
+# Default: true
+cookies-store = true
+
+# host-block-lists (List of Url):
+# List of URLs of lists which contain hosts to block.
+# The file can be in one of the following formats:
+# - An '/etc/hosts'-like file
+# - One host per line
+# - A zip-file of any of the above, with either only one file, or a
+# file named 'hosts' (with any extension).
+# Default:
+# https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext
+host-block-lists = https://www.malwaredomainlist.com/hostslist/hosts.txt,http://someonewhocares.org/hosts/hosts,http://winhelp2002.mvps.org/hosts.zip,http://malwaredomains.lehigh.edu/files/justdomains.zip,https://pgl.yoyo.org/adservers/serverlist.php?hostformat=hosts&mimetype=plaintext
+
+# host-blocking-enabled (Bool):
+# Whether host blocking is enabled.
+# Valid values: true, false
+# Default: true
+host-blocking-enabled = false
+
+# host-blocking-whitelist (List of String):
+# List of domains that should always be loaded, despite being
+# ad-blocked.
+# Domains may contain * and ? wildcards and are otherwise required
+# to exactly match the requested domain.
+# Local domains are always exempt from hostblocking.
+# Default: piwik.org
+host-blocking-whitelist = piwik.org,next-episode.net,churchmilitant.tv,load.sumome.com,s7.addthis.com,www.google-analytics.com,ssl.google-analytics.com,referrer.disqus.com,m.addthisedge.com,m.addthis.com,stats.g.doubleclick.net,*.addthis.com,lukesmith.xyz
+
+# enable-pdfjs (Bool):
+# Enable 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.
+# Valid values: true, false
+# Default: false
+enable-pdfjs = false
+
+
+# Hinting settings.
+[hints]
+
+# border (String):
+# CSS border value for hints.
+# Default: 1px solid #E3BE23
+border = 1px solid #E3BE23
+
+# mode (String):
+# Mode to use for hints.
+# number: Use numeric hints. (In this mode you can also type
+# letters form the hinted element to filter and reduce the number of
+# elements that are hinted.)
+# letter: Use the chars in the hints -> chars setting.
+# word: Use hints words based on the html elements and the extra
+# words.
+# Default: letter
+mode = number
+
+# chars (UniqueCharString):
+# Chars used for hint strings.
+# Default: asdfghjkl
+chars = asdfghjkl
+
+# min-chars (Int):
+# Minimum number of chars used for hint strings.
+# Default: 1
+min-chars = 1
+
+# scatter (Bool):
+# Whether to scatter hint key chains (like Vimium) or not (like
+# dwb). Ignored for number hints.
+# Valid values: true, false
+# Default: true
+scatter = true
+
+# uppercase (Bool):
+# Make chars in hint strings uppercase.
+# Valid values: true, false
+# Default: false
+uppercase = false
+
+# dictionary (File):
+# The dictionary file to be used by the word hints.
+# Default: /usr/share/dict/words
+dictionary = /usr/share/dict/words
+
+# auto-follow (String):
+# Controls when a hint can be automatically followed without the
+# user pressing Enter.
+# always: Auto-follow whenever there is only a single hint on a
+# page.
+# unique-match: Auto-follow whenever there is a unique non-empty
+# match in either the hint string (word mode) or filter (number
+# mode).
+# full-match: Follow the hint when the user typed the whole hint
+# (letter, word or number mode) or the element's text (only in
+# number mode).
+# never: The user will always need to press Enter to follow a
+# hint.
+# Default: unique-match
+auto-follow = unique-match
+
+# auto-follow-timeout (Int):
+# A timeout (in milliseconds) to inhibit normal-mode key bindings
+# after a successful auto-follow.
+# Default: 0
+auto-follow-timeout = 0
+
+# next-regexes (List of Regex):
+# A comma-separated list of regexes to use for 'next' links.
+# Default:
+# \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b
+next-regexes = \bnext\b,\bmore\b,\bnewer\b,\b[>→≫]\b,\b(>>|»)\b,\bcontinue\b
+
+# prev-regexes (List of Regex):
+# A comma-separated list of regexes to use for 'prev' links.
+# Default: \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b
+prev-regexes = \bprev(ious)?\b,\bback\b,\bolder\b,\b[<←≪]\b,\b(<<|«)\b
+
+# find-implementation (String):
+# Which implementation to use to find elements to hint.
+# javascript: Better but slower
+# python: Slightly worse but faster
+# Default: python
+find-implementation = python
+
+# hide-unmatched-rapid-hints (Bool):
+# Controls hiding unmatched hints in rapid mode.
+# Valid values: true, false
+# Default: true
+hide-unmatched-rapid-hints = true
+
+
+# Definitions of search engines which can be used via the address bar.
+# The searchengine named `DEFAULT` is used when `general -> auto-search`
+# is true and something else than a URL was entered to be opened. Other
+# search engines can be used by prepending the search engine name to the
+# search term, e.g. `:open google qutebrowser`. The string `{}` will be
+# replaced by the search term, use `{{` and `}}` for literal `{`/`}`
+# signs.
+[searchengines]
+DEFAULT = https://duckduckgo.com/?q={}
+
+
+# Aliases for commands.
+# By default, no aliases are defined. Example which adds a new command
+# `:qtb` to open qutebrowsers website:
+# `qtb = open https://www.qutebrowser.org/`
+[aliases]
+qtb = open https://www.qutebrowser.org/
+mbt = open t https://signin1.bt.com/login/emailloginform
+Ombt = open -t https://signin1.bt.com/login/emailloginform
+nx = open http://next-episode.net/
+Onx = open -t http://next-episode.net/
+gk = open https://gameknot.com/
+Ogk = open -t https://gameknot.com/
+yt = open https://youtube.com
+Oyt = open -t https://youtube.com
+Y = open http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files
+OY = open -t http://www.cosmos.esa.int/web/xmm-newton/current-calibration-files
+1337 = open https://1337x.to/home
+O1337 = open -t https://1337x.to/home
+ez = open https://eztv.ag
+Oez = open -t https://eztv.ag
+tz = open https://extratorrent.ag
+Otz = open -t https://extratorrent.ag
+tpb = open https://thepiratebay.org/
+Otpb = open -t https://thepiratebay.org/
+Nm = open https://netmail.herts.ac.uk/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fnetmail.herts.ac.uk%2fowa
+ONm = open -t https://netmail.herts.ac.uk/owa/auth/logon.aspx?replaceCurrent=1&url=https%3a%2f%2fnetmail.herts.ac.uk%2fowa
+
+
+# Colors used in the UI.
+# A value can be in one of the following format:
+# * `#RGB`/`#RRGGBB`/`#RRRGGGBBB`/`#RRRRGGGGBBBB`
+# * An SVG color name as specified in http://www.w3.org/TR/SVG/types.html#ColorKeywords[the W3C specification].
+# * transparent (no color)
+# * `rgb(r, g, b)` / `rgba(r, g, b, a)` (values 0-255 or percentages)
+# * `hsv(h, s, v)` / `hsva(h, s, v, a)` (values 0-255, hue 0-359)
+# * A gradient as explained in http://doc.qt.io/qt-5/stylesheet-reference.html#list-of-property-types[the Qt documentation] under ``Gradient''.
+# A *.system value determines the color system to use for color
+# interpolation between similarly-named *.start and *.stop entries,
+# regardless of how they are defined in the options. Valid values are
+# 'rgb', 'hsv', and 'hsl'.
+# The `hints.*` values are a special case as they're real CSS colors, not Qt-CSS colors. There, for a gradient, you need to use `-webkit-gradient`, see https://www.webkit.org/blog/175/introducing-css-gradients/[the WebKit documentation].
+[colors]
+
+# completion.fg (QtColor):
+# Text color of the completion widget.
+# Default: white
+completion.fg = white
+
+# completion.bg (QssColor):
+# Background color of the completion widget.
+# Default: #333333
+completion.bg = #333333
+
+# completion.alternate-bg (QssColor):
+# Alternating background color of the completion widget.
+# Default: #444444
+completion.alternate-bg = #444444
+
+# completion.category.fg (QtColor):
+# Foreground color of completion widget category headers.
+# Default: white
+completion.category.fg = white
+
+# completion.category.bg (QssColor):
+# Background color of the completion widget category headers.
+# Default: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888,
+# stop:1 #505050)
+completion.category.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 #888888, stop:1 #505050)
+
+# completion.category.border.top (QssColor):
+# Top border color of the completion widget category headers.
+# Default: black
+completion.category.border.top = black
+
+# completion.category.border.bottom (QssColor):
+# Bottom border color of the completion widget category headers.
+# Default: ${completion.category.border.top}
+completion.category.border.bottom = ${completion.category.border.top}
+
+# completion.item.selected.fg (QtColor):
+# Foreground color of the selected completion item.
+# Default: black
+completion.item.selected.fg = black
+
+# completion.item.selected.bg (QssColor):
+# Background color of the selected completion item.
+# Default: #e8c000
+completion.item.selected.bg = #e8c000
+
+# completion.item.selected.border.top (QssColor):
+# Top border color of the completion widget category headers.
+# Default: #bbbb00
+completion.item.selected.border.top = #bbbb00
+
+# completion.item.selected.border.bottom (QssColor):
+# Bottom border color of the selected completion item.
+# Default: ${completion.item.selected.border.top}
+completion.item.selected.border.bottom = ${completion.item.selected.border.top}
+
+# completion.match.fg (QssColor):
+# Foreground color of the matched text in the completion.
+# Default: #ff4444
+completion.match.fg = #ff4444
+
+# completion.scrollbar.fg (QssColor):
+# Color of the scrollbar handle in completion view.
+# Default: ${completion.fg}
+completion.scrollbar.fg = ${completion.fg}
+
+# completion.scrollbar.bg (QssColor):
+# Color of the scrollbar in completion view
+# Default: ${completion.bg}
+completion.scrollbar.bg = ${completion.bg}
+
+# statusbar.fg (QssColor):
+# Foreground color of the statusbar.
+# Default: white
+statusbar.fg = white
+
+# statusbar.bg (QssColor):
+# Background color of the statusbar.
+# Default: black
+statusbar.bg = black
+
+# statusbar.fg.private (QssColor):
+# Foreground color of the statusbar in private browsing mode.
+# Default: ${statusbar.fg}
+statusbar.fg.private = ${statusbar.fg}
+
+# statusbar.bg.private (QssColor):
+# Background color of the statusbar in private browsing mode.
+# Default: #666666
+statusbar.bg.private = #666666
+
+# statusbar.fg.insert (QssColor):
+# Foreground color of the statusbar in insert mode.
+# Default: ${statusbar.fg}
+statusbar.fg.insert = ${statusbar.fg}
+
+# statusbar.bg.insert (QssColor):
+# Background color of the statusbar in insert mode.
+# Default: darkgreen
+statusbar.bg.insert = darkgreen
+
+# statusbar.fg.command (QssColor):
+# Foreground color of the statusbar in command mode.
+# Default: ${statusbar.fg}
+statusbar.fg.command = ${statusbar.fg}
+
+# statusbar.bg.command (QssColor):
+# Background color of the statusbar in command mode.
+# Default: ${statusbar.bg}
+statusbar.bg.command = ${statusbar.bg}
+
+# statusbar.fg.command.private (QssColor):
+# Foreground color of the statusbar in private browsing + command
+# mode.
+# Default: ${statusbar.fg.private}
+statusbar.fg.command.private = ${statusbar.fg.private}
+
+# statusbar.bg.command.private (QssColor):
+# Background color of the statusbar in private browsing + command
+# mode.
+# Default: ${statusbar.bg.private}
+statusbar.bg.command.private = ${statusbar.bg.private}
+
+# statusbar.fg.caret (QssColor):
+# Foreground color of the statusbar in caret mode.
+# Default: ${statusbar.fg}
+statusbar.fg.caret = ${statusbar.fg}
+
+# statusbar.bg.caret (QssColor):
+# Background color of the statusbar in caret mode.
+# Default: purple
+statusbar.bg.caret = purple
+
+# statusbar.fg.caret-selection (QssColor):
+# Foreground color of the statusbar in caret mode with a selection
+# Default: ${statusbar.fg}
+statusbar.fg.caret-selection = ${statusbar.fg}
+
+# statusbar.bg.caret-selection (QssColor):
+# Background color of the statusbar in caret mode with a selection
+# Default: #a12dff
+statusbar.bg.caret-selection = #a12dff
+
+# statusbar.progress.bg (QssColor):
+# Background color of the progress bar.
+# Default: white
+statusbar.progress.bg = white
+
+# statusbar.url.fg (QssColor):
+# Default foreground color of the URL in the statusbar.
+# Default: ${statusbar.fg}
+statusbar.url.fg = ${statusbar.fg}
+
+# statusbar.url.fg.success (QssColor):
+# Foreground color of the URL in the statusbar on successful load
+# (http).
+# Default: white
+statusbar.url.fg.success = white
+
+# statusbar.url.fg.success.https (QssColor):
+# Foreground color of the URL in the statusbar on successful load
+# (https).
+# Default: lime
+statusbar.url.fg.success.https = lime
+
+# statusbar.url.fg.error (QssColor):
+# Foreground color of the URL in the statusbar on error.
+# Default: orange
+statusbar.url.fg.error = orange
+
+# statusbar.url.fg.warn (QssColor):
+# Foreground color of the URL in the statusbar when there's a
+# warning.
+# Default: yellow
+statusbar.url.fg.warn = yellow
+
+# statusbar.url.fg.hover (QssColor):
+# Foreground color of the URL in the statusbar for hovered links.
+# Default: aqua
+statusbar.url.fg.hover = aqua
+
+# tabs.fg.odd (QtColor):
+# Foreground color of unselected odd tabs.
+# Default: white
+tabs.fg.odd = white
+
+# tabs.bg.odd (QtColor):
+# Background color of unselected odd tabs.
+# Default: grey
+tabs.bg.odd = grey
+
+# tabs.fg.even (QtColor):
+# Foreground color of unselected even tabs.
+# Default: white
+tabs.fg.even = white
+
+# tabs.bg.even (QtColor):
+# Background color of unselected even tabs.
+# Default: darkgrey
+tabs.bg.even = darkgrey
+
+# tabs.fg.selected.odd (QtColor):
+# Foreground color of selected odd tabs.
+# Default: white
+tabs.fg.selected.odd = white
+
+# tabs.bg.selected.odd (QtColor):
+# Background color of selected odd tabs.
+# Default: black
+tabs.bg.selected.odd = black
+
+# tabs.fg.selected.even (QtColor):
+# Foreground color of selected even tabs.
+# Default: ${tabs.fg.selected.odd}
+tabs.fg.selected.even = ${tabs.fg.selected.odd}
+
+# tabs.bg.selected.even (QtColor):
+# Background color of selected even tabs.
+# Default: ${tabs.bg.selected.odd}
+tabs.bg.selected.even = ${tabs.bg.selected.odd}
+
+# tabs.bg.bar (QtColor):
+# Background color of the tab bar.
+# Default: #555555
+tabs.bg.bar = #555555
+
+# tabs.indicator.start (QtColor):
+# Color gradient start for the tab indicator.
+# Default: #0000aa
+tabs.indicator.start = #0000aa
+
+# tabs.indicator.stop (QtColor):
+# Color gradient end for the tab indicator.
+# Default: #00aa00
+tabs.indicator.stop = #00aa00
+
+# tabs.indicator.error (QtColor):
+# Color for the tab indicator on errors..
+# Default: #ff0000
+tabs.indicator.error = #ff0000
+
+# tabs.indicator.system (ColorSystem):
+# Color gradient interpolation system for the tab indicator.
+# rgb: Interpolate in the RGB color system.
+# hsv: Interpolate in the HSV color system.
+# hsl: Interpolate in the HSL color system.
+# none: Don't show a gradient.
+# Default: rgb
+tabs.indicator.system = rgb
+
+# hints.fg (QssColor):
+# Font color for hints.
+# Default: black
+hints.fg = black
+
+# hints.bg (QssColor):
+# Background color for hints. Note that you can use a `rgba(...)`
+# value for transparency.
+# Default: qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255,
+# 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8))
+hints.bg = qlineargradient(x1:0, y1:0, x2:0, y2:1, stop:0 rgba(255, 247, 133, 0.8), stop:1 rgba(255, 197, 66, 0.8))
+
+# hints.fg.match (QssColor):
+# Font color for the matched part of hints.
+# Default: green
+hints.fg.match = green
+
+# downloads.bg.bar (QssColor):
+# Background color for the download bar.
+# Default: black
+downloads.bg.bar = black
+
+# downloads.fg.start (QtColor):
+# Color gradient start for download text.
+# Default: white
+downloads.fg.start = white
+
+# downloads.bg.start (QtColor):
+# Color gradient start for download backgrounds.
+# Default: #0000aa
+downloads.bg.start = #0000aa
+
+# downloads.fg.stop (QtColor):
+# Color gradient end for download text.
+# Default: ${downloads.fg.start}
+downloads.fg.stop = ${downloads.fg.start}
+
+# downloads.bg.stop (QtColor):
+# Color gradient stop for download backgrounds.
+# Default: #00aa00
+downloads.bg.stop = #00aa00
+
+# downloads.fg.system (ColorSystem):
+# Color gradient interpolation system for download text.
+# rgb: Interpolate in the RGB color system.
+# hsv: Interpolate in the HSV color system.
+# hsl: Interpolate in the HSL color system.
+# none: Don't show a gradient.
+# Default: rgb
+downloads.fg.system = rgb
+
+# downloads.bg.system (ColorSystem):
+# Color gradient interpolation system for download backgrounds.
+# rgb: Interpolate in the RGB color system.
+# hsv: Interpolate in the HSV color system.
+# hsl: Interpolate in the HSL color system.
+# none: Don't show a gradient.
+# Default: rgb
+downloads.bg.system = rgb
+
+# downloads.fg.error (QtColor):
+# Foreground color for downloads with errors.
+# Default: white
+downloads.fg.error = white
+
+# downloads.bg.error (QtColor):
+# Background color for downloads with errors.
+# Default: red
+downloads.bg.error = red
+
+# webpage.bg (QtColor):
+# Background color for webpages if unset (or empty to use the
+# theme's color)
+# Default: white
+webpage.bg = white
+
+# keyhint.fg (QssColor):
+# Text color for the keyhint widget.
+# Default: #FFFFFF
+keyhint.fg = #FFFFFF
+
+# keyhint.fg.suffix (CssColor):
+# Highlight color for keys to complete the current keychain
+# Default: #FFFF00
+keyhint.fg.suffix = #FFFF00
+
+# keyhint.bg (QssColor):
+# Background color of the keyhint widget.
+# Default: rgba(0, 0, 0, 80%)
+keyhint.bg = rgba(0, 0, 0, 80%)
+
+# messages.fg.error (QssColor):
+# Foreground color of an error message.
+# Default: white
+messages.fg.error = white
+
+# messages.bg.error (QssColor):
+# Background color of an error message.
+# Default: red
+messages.bg.error = red
+
+# messages.border.error (QssColor):
+# Border color of an error message.
+# Default: #bb0000
+messages.border.error = #bb0000
+
+# messages.fg.warning (QssColor):
+# Foreground color a warning message.
+# Default: white
+messages.fg.warning = white
+
+# messages.bg.warning (QssColor):
+# Background color of a warning message.
+# Default: darkorange
+messages.bg.warning = darkorange
+
+# messages.border.warning (QssColor):
+# Border color of an error message.
+# Default: #d47300
+messages.border.warning = #d47300
+
+# messages.fg.info (QssColor):
+# Foreground color an info message.
+# Default: white
+messages.fg.info = white
+
+# messages.bg.info (QssColor):
+# Background color of an info message.
+# Default: black
+messages.bg.info = black
+
+# messages.border.info (QssColor):
+# Border color of an info message.
+# Default: #333333
+messages.border.info = #333333
+
+# prompts.fg (QssColor):
+# Foreground color for prompts.
+# Default: white
+prompts.fg = white
+
+# prompts.bg (QssColor):
+# Background color for prompts.
+# Default: darkblue
+prompts.bg = darkblue
+
+# prompts.selected.bg (QssColor):
+# Background color for the selected item in filename prompts.
+# Default: #308cc6
+prompts.selected.bg = #308cc6
+
+
+# Fonts used for the UI, with optional style/weight/size.
+# * Style: `normal`/`italic`/`oblique`
+# * Weight: `normal`, `bold`, `100`..`900`
+# * Size: _number_ `px`/`pt`
+[fonts]
+
+# _monospace (Font):
+# Default monospace fonts.
+# Default: xos4 Terminus, Terminus, Monospace, "DejaVu Sans Mono",
+# Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New",
+# Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal
+_monospace = Terminus, Monospace, "DejaVu Sans Mono", Monaco, "Bitstream Vera Sans Mono", "Andale Mono", "Courier New", Courier, "Liberation Mono", monospace, Fixed, Consolas, Terminal
+
+# completion (Font):
+# Font used in the completion widget.
+# Default: 8pt ${_monospace}
+completion = 8pt ${_monospace}
+
+# completion.category (Font):
+# Font used in the completion categories.
+# Default: bold ${completion}
+completion.category = bold ${completion}
+
+# tabbar (QtFont):
+# Font used in the tab bar.
+# Default: 8pt ${_monospace}
+tabbar = 8pt ${_monospace}
+
+# statusbar (Font):
+# Font used in the statusbar.
+# Default: 8pt ${_monospace}
+statusbar = 8pt ${_monospace}
+
+# downloads (Font):
+# Font used for the downloadbar.
+# Default: 8pt ${_monospace}
+downloads = 8pt ${_monospace}
+
+# hints (Font):
+# Font used for the hints.
+# Default: bold 13px ${_monospace}
+hints = bold 13px ${_monospace}
+
+# debug-console (QtFont):
+# Font used for the debugging console.
+# Default: 8pt ${_monospace}
+debug-console = 8pt ${_monospace}
+
+# web-family-standard (FontFamily):
+# Font family for standard fonts.
+# Default:
+web-family-standard =
+
+# web-family-fixed (FontFamily):
+# Font family for fixed fonts.
+# Default:
+web-family-fixed =
+
+# web-family-serif (FontFamily):
+# Font family for serif fonts.
+# Default:
+web-family-serif =
+
+# web-family-sans-serif (FontFamily):
+# Font family for sans-serif fonts.
+# Default:
+web-family-sans-serif =
+
+# web-family-cursive (FontFamily):
+# Font family for cursive fonts.
+# Default:
+web-family-cursive =
+
+# web-family-fantasy (FontFamily):
+# Font family for fantasy fonts.
+# Default:
+web-family-fantasy =
+
+# web-size-minimum (Int):
+# The hard minimum font size.
+# Default: 0
+web-size-minimum = 0
+
+# web-size-minimum-logical (Int):
+# The minimum logical font size that is applied when zooming out.
+# Default: 6
+web-size-minimum-logical = 6
+
+# web-size-default (Int):
+# The default font size for regular text.
+# Default: 16
+web-size-default = 16
+
+# web-size-default-fixed (Int):
+# The default font size for fixed-pitch text.
+# Default: 13
+web-size-default-fixed = 13
+
+# keyhint (Font):
+# Font used in the keyhint widget.
+# Default: 8pt ${_monospace}
+keyhint = 8pt ${_monospace}
+
+# messages.error (Font):
+# Font used for error messages.
+# Default: 8pt ${_monospace}
+messages.error = 8pt ${_monospace}
+
+# messages.warning (Font):
+# Font used for warning messages.
+# Default: 8pt ${_monospace}
+messages.warning = 8pt ${_monospace}
+
+# messages.info (Font):
+# Font used for info messages.
+# Default: 8pt ${_monospace}
+messages.info = 8pt ${_monospace}
+
+# prompts (Font):
+# Font used for prompts.
+# Default: 8pt sans-serif
+prompts = 8pt sans-serif
diff --git a/.config/qutebrowser/scripts/__init__.py b/.config/qutebrowser/scripts/__init__.py
new file mode 100755
index 0000000..90be1e0
--- /dev/null
+++ b/.config/qutebrowser/scripts/__init__.py
@@ -0,0 +1,3 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+"""Various utility scripts."""
diff --git a/.config/qutebrowser/scripts/asciidoc2html.py b/.config/qutebrowser/scripts/asciidoc2html.py
new file mode 100755
index 0000000..c4af174
--- /dev/null
+++ b/.config/qutebrowser/scripts/asciidoc2html.py
@@ -0,0 +1,303 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Generate the html documentation based on the asciidoc files."""
+
+import re
+import os
+import os.path
+import sys
+import subprocess
+import glob
+import shutil
+import tempfile
+import argparse
+import io
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
+
+from scripts import utils
+
+
+class AsciiDoc:
+
+ """Abstraction of an asciidoc subprocess."""
+
+ FILES = ['faq', 'changelog', 'contributing', 'quickstart', 'userscripts']
+
+ def __init__(self, args):
+ self._cmd = None
+ self._args = args
+ self._homedir = None
+ self._themedir = None
+ self._tempdir = None
+ self._failed = False
+
+ def prepare(self):
+ """Get the asciidoc command and create the homedir to use."""
+ self._cmd = self._get_asciidoc_cmd()
+ self._homedir = tempfile.mkdtemp()
+ self._themedir = os.path.join(
+ self._homedir, '.asciidoc', 'themes', 'qute')
+ self._tempdir = os.path.join(self._homedir, 'tmp')
+ os.makedirs(self._tempdir)
+ os.makedirs(self._themedir)
+
+ def cleanup(self):
+ """Clean up the temporary home directory for asciidoc."""
+ if self._homedir is not None and not self._failed:
+ shutil.rmtree(self._homedir)
+
+ def build(self):
+ """Build either the website or the docs."""
+ if self._args.website:
+ self._build_website()
+ else:
+ self._build_docs()
+ self._copy_images()
+
+ def _build_docs(self):
+ """Render .asciidoc files to .html sites."""
+ files = [('doc/{}.asciidoc'.format(f),
+ 'qutebrowser/html/doc/{}.html'.format(f))
+ for f in self.FILES]
+ for src in glob.glob('doc/help/*.asciidoc'):
+ name, _ext = os.path.splitext(os.path.basename(src))
+ dst = 'qutebrowser/html/doc/{}.html'.format(name)
+ files.append((src, dst))
+
+ # patch image links to use local copy
+ replacements = [
+ ("https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-big.png",
+ "qute://help/img/cheatsheet-big.png"),
+ ("https://raw.githubusercontent.com/qutebrowser/qutebrowser/master/doc/img/cheatsheet-small.png",
+ "qute://help/img/cheatsheet-small.png")
+ ]
+ asciidoc_args = ['-a', 'source-highlighter=pygments']
+
+ for src, dst in files:
+ src_basename = os.path.basename(src)
+ modified_src = os.path.join(self._tempdir, src_basename)
+ with open(modified_src, 'w', encoding='utf-8') as modified_f, \
+ open(src, 'r', encoding='utf-8') as f:
+ for line in f:
+ for orig, repl in replacements:
+ line = line.replace(orig, repl)
+ modified_f.write(line)
+ self.call(modified_src, dst, *asciidoc_args)
+
+ def _copy_images(self):
+ """Copy image files to qutebrowser/html/doc."""
+ print("Copying files...")
+ dst_path = os.path.join('qutebrowser', 'html', 'doc', 'img')
+ try:
+ os.mkdir(dst_path)
+ except FileExistsError:
+ pass
+ for filename in ['cheatsheet-big.png', 'cheatsheet-small.png']:
+ src = os.path.join('doc', 'img', filename)
+ dst = os.path.join(dst_path, filename)
+ shutil.copy(src, dst)
+
+ def _build_website_file(self, root, filename):
+ """Build a single website file."""
+ src = os.path.join(root, filename)
+ src_basename = os.path.basename(src)
+ parts = [self._args.website[0]]
+ dirname = os.path.dirname(src)
+ if dirname:
+ parts.append(os.path.relpath(os.path.dirname(src)))
+ parts.append(
+ os.extsep.join((os.path.splitext(src_basename)[0],
+ 'html')))
+ dst = os.path.join(*parts)
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
+
+ modified_src = os.path.join(self._tempdir, src_basename)
+ shutil.copy('www/header.asciidoc', modified_src)
+
+ outfp = io.StringIO()
+
+ with open(modified_src, 'r', encoding='utf-8') as header_file:
+ header = header_file.read()
+ header += "\n\n"
+
+ with open(src, 'r', encoding='utf-8') as infp:
+ outfp.write("\n\n")
+ hidden = False
+ found_title = False
+ title = ""
+ last_line = ""
+
+ for line in infp:
+ line = line.rstrip()
+ if line == '// QUTE_WEB_HIDE':
+ assert not hidden
+ hidden = True
+ elif line == '// QUTE_WEB_HIDE_END':
+ assert hidden
+ hidden = False
+ elif line == "The Compiler <mail@qutebrowser.org>":
+ continue
+ elif re.fullmatch(r':\w+:.*', line):
+ # asciidoc field
+ continue
+
+ if not found_title:
+ if re.fullmatch(r'=+', line):
+ line = line.replace('=', '-')
+ found_title = True
+ title = last_line + " | qutebrowser\n"
+ title += "=" * (len(title) - 1)
+ elif re.fullmatch(r'= .+', line):
+ line = '==' + line[1:]
+ found_title = True
+ title = last_line + " | qutebrowser\n"
+ title += "=" * (len(title) - 1)
+
+ if not hidden:
+ outfp.write(line.replace(".asciidoc[", ".html[") + '\n')
+ last_line = line
+
+ current_lines = outfp.getvalue()
+ outfp.close()
+
+ with open(modified_src, 'w+', encoding='utf-8') as final_version:
+ final_version.write(title + "\n\n" + header + current_lines)
+
+ asciidoc_args = ['--theme=qute', '-a toc', '-a toc-placement=manual',
+ '-a', 'source-highlighter=pygments']
+ self.call(modified_src, dst, *asciidoc_args)
+
+ def _build_website(self):
+ """Prepare and build the website."""
+ theme_file = os.path.abspath(os.path.join('www', 'qute.css'))
+ shutil.copy(theme_file, self._themedir)
+
+ outdir = self._args.website[0]
+
+ for root, _dirs, files in os.walk(os.getcwd()):
+ for filename in files:
+ basename, ext = os.path.splitext(filename)
+ if (ext != '.asciidoc' or
+ basename in ['header', 'OpenSans-License']):
+ continue
+ self._build_website_file(root, filename)
+
+ copy = {'icons': 'icons', 'doc/img': 'doc/img', 'www/media': 'media/'}
+
+ for src, dest in copy.items():
+ full_dest = os.path.join(outdir, dest)
+ try:
+ shutil.rmtree(full_dest)
+ except FileNotFoundError:
+ pass
+ shutil.copytree(src, full_dest)
+
+ for dst, link_name in [
+ ('README.html', 'index.html'),
+ (os.path.join('doc', 'quickstart.html'), 'quickstart.html')]:
+ try:
+ os.symlink(dst, os.path.join(outdir, link_name))
+ except FileExistsError:
+ pass
+
+ def _get_asciidoc_cmd(self):
+ """Try to find out what commandline to use to invoke asciidoc."""
+ if self._args.asciidoc is not None:
+ return self._args.asciidoc
+
+ try:
+ subprocess.run(['asciidoc'], stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL)
+ except OSError:
+ pass
+ else:
+ return ['asciidoc']
+
+ try:
+ subprocess.run(['asciidoc.py'], stdout=subprocess.DEVNULL,
+ stderr=subprocess.DEVNULL)
+ except OSError:
+ pass
+ else:
+ return ['asciidoc.py']
+
+ raise FileNotFoundError
+
+ def call(self, src, dst, *args):
+ """Call asciidoc for the given files.
+
+ Args:
+ src: The source .asciidoc file.
+ dst: The destination .html file, or None to auto-guess.
+ *args: Additional arguments passed to asciidoc.
+ """
+ print("Calling asciidoc for {}...".format(os.path.basename(src)))
+ cmdline = self._cmd[:]
+ if dst is not None:
+ cmdline += ['--out-file', dst]
+ cmdline += args
+ cmdline.append(src)
+ try:
+ env = os.environ.copy()
+ env['HOME'] = self._homedir
+ subprocess.run(cmdline, check=True, env=env)
+ except (subprocess.CalledProcessError, OSError) as e:
+ self._failed = True
+ utils.print_col(str(e), 'red')
+ print("Keeping modified sources in {}.".format(self._homedir))
+ sys.exit(1)
+
+
+def main(colors=False):
+ """Generate html files for the online documentation."""
+ utils.change_cwd()
+ utils.use_color = colors
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--website', help="Build website into a given "
+ "directory.", nargs=1)
+ parser.add_argument('--asciidoc', help="Full path to python and "
+ "asciidoc.py. If not given, it's searched in PATH.",
+ nargs=2, required=False,
+ metavar=('PYTHON', 'ASCIIDOC'))
+ args = parser.parse_args()
+ try:
+ os.mkdir('qutebrowser/html/doc')
+ except FileExistsError:
+ pass
+
+ asciidoc = AsciiDoc(args)
+ try:
+ asciidoc.prepare()
+ except FileNotFoundError:
+ utils.print_col("Could not find asciidoc! Please install it, or use "
+ "the --asciidoc argument to point this script to the "
+ "correct python/asciidoc.py location!", 'red')
+ sys.exit(1)
+
+ try:
+ asciidoc.build()
+ finally:
+ asciidoc.cleanup()
+
+
+if __name__ == '__main__':
+ main(colors=True)
diff --git a/.config/qutebrowser/scripts/cycle-inputs.js b/.config/qutebrowser/scripts/cycle-inputs.js
new file mode 100644
index 0000000..bb667bd
--- /dev/null
+++ b/.config/qutebrowser/scripts/cycle-inputs.js
@@ -0,0 +1,46 @@
+/* Cycle <input> text boxes.
+ * works with the types defined in 'types'.
+ * Note: Does not work for <textarea>.
+ *
+ * Example keybind:
+ * CYCLE_INPUTS = "jseval -q -f ~/.config/qutebrowser/cycle-inputs.js"
+ * config.bind('gi', CYCLE_INPUTS)
+ *
+ * By dive on freenode <dave@dawoodfall.net>
+ */
+
+(function() {
+ "use strict";
+ const inputs = document.getElementsByTagName("input");
+ const types = /text|password|date|email|month|number|range|search|tel|time|url|week/;
+ const hidden = /hidden/;
+ let found = false;
+
+ function ishidden(el) {
+ return hidden.test(el.attributes.value) || el.offsetParent === null;
+ }
+
+ for (let i = 0; i < inputs.length; i++) {
+ if (inputs[i] === document.activeElement) {
+ for (let k = i + 1; k < inputs.length; k++) {
+ if (!ishidden(inputs[k]) && types.test(inputs[k].type)) {
+ inputs[k].focus();
+ found = true;
+ break;
+ }
+ }
+ break;
+ }
+ }
+
+ if (!found) {
+ for (let i = 0; i < inputs.length; i++) {
+ if (!ishidden(inputs[i]) && types.test(inputs[i].type)) {
+ inputs[i].focus();
+ break;
+ }
+ }
+ }
+})();
+
+// vim: tw=0 expandtab tabstop=4 softtabstop=4 shiftwidth=4
diff --git a/.config/qutebrowser/scripts/dev/Makefile-dmg b/.config/qutebrowser/scripts/dev/Makefile-dmg
new file mode 100644
index 0000000..1cf4cfb
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/Makefile-dmg
@@ -0,0 +1,71 @@
+#
+# Build file for creating DMG files.
+#
+# The DMG packager looks for a template.dmg.bz2 for using as its
+# DMG template. If it doesn't find one, it generates a clean one.
+#
+# If you create a DMG template, you should make one containing all
+# the files listed in $(SOURCE_FILES) below, and arrange everything to suit
+# your style. The contents of the files themselves does not matter, so
+# they can be empty (they will be overwritten later).
+#
+# Remko Tronçon
+# https://el-tramo.be
+# Licensed under the MIT License. See COPYING for details.
+
+
+################################################################################
+# Customizable variables
+################################################################################
+
+NAME ?= qutebrowser
+
+SOURCE_DIR ?= .
+SOURCE_FILES ?= dist/qutebrowser.app LICENSE
+
+TEMPLATE_DMG ?= template.dmg
+TEMPLATE_SIZE ?= 300m
+
+################################################################################
+# DMG building. No editing should be needed beyond this point.
+################################################################################
+
+MASTER_DMG=$(NAME).dmg
+WC_DMG=wc.dmg
+WC_DIR=wc
+
+.PHONY: all
+all: $(MASTER_DMG)
+
+$(TEMPLATE_DMG):
+ @echo
+ @echo --------------------- Generating empty template --------------------
+ mkdir template
+ hdiutil create -fs HFSX -layout SPUD -size $(TEMPLATE_SIZE) "$(TEMPLATE_DMG)" -srcfolder template -format UDRW -volname "$(NAME)" -quiet
+ rmdir template
+
+$(WC_DMG): $(TEMPLATE_DMG)
+ cp $< $@
+
+$(MASTER_DMG): $(WC_DMG) $(addprefix $(SOURCE_DIR)/,$(SOURCE_FILES))
+ @echo
+ @echo --------------------- Creating Disk Image --------------------
+ mkdir -p $(WC_DIR)
+ hdiutil attach "$(WC_DMG)" -noautoopen -quiet -mountpoint "$(WC_DIR)"
+ for i in $(SOURCE_FILES); do \
+ rm -rf "$(WC_DIR)/$$i"; \
+ ditto -rsrc "$(SOURCE_DIR)/$$i" "$(WC_DIR)/$${i##*/}"; \
+ done
+ ln -s /Applications $(WC_DIR)
+ #rm -f "$@"
+ #hdiutil create -srcfolder "$(WC_DIR)" -format UDZO -imagekey zlib-level=9 "$@" -volname "$(NAME) $(VERSION)" -scrub -quiet
+ WC_DEV=`hdiutil info | grep "$(WC_DIR)" | grep "Apple_HFS" | awk '{print $$1}'` && \
+ hdiutil detach $$WC_DEV -quiet -force
+ rm -f "$(MASTER_DMG)"
+ hdiutil convert "$(WC_DMG)" -quiet -format UDZO -imagekey zlib-level=9 -o "$@"
+ rm -rf $(WC_DIR)
+ @echo
+
+.PHONY: clean
+clean:
+ -rm -rf $(TEMPLATE_DMG) $(MASTER_DMG) $(WC_DMG)
diff --git a/.config/qutebrowser/scripts/dev/__init__.py b/.config/qutebrowser/scripts/dev/__init__.py
new file mode 100644
index 0000000..7dc0433
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/__init__.py
@@ -0,0 +1,3 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+"""Various scripts used for developing qutebrowser."""
diff --git a/.config/qutebrowser/scripts/dev/build_release.py b/.config/qutebrowser/scripts/dev/build_release.py
new file mode 100755
index 0000000..254132b
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/build_release.py
@@ -0,0 +1,419 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Build a new release."""
+
+
+import os
+import os.path
+import sys
+import time
+import glob
+import shutil
+import plistlib
+import subprocess
+import argparse
+import tarfile
+import tempfile
+import collections
+
+try:
+ import winreg
+except ImportError:
+ pass
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+import qutebrowser
+from scripts import utils
+# from scripts.dev import update_3rdparty
+
+
+def call_script(name, *args, python=sys.executable):
+ """Call a given shell script.
+
+ Args:
+ name: The script to call.
+ *args: The arguments to pass.
+ python: The python interpreter to use.
+ """
+ path = os.path.join(os.path.dirname(__file__), os.pardir, name)
+ subprocess.run([python, path] + list(args), check=True)
+
+
+def call_tox(toxenv, *args, python=sys.executable):
+ """Call tox.
+
+ Args:
+ toxenv: Which tox environment to use
+ *args: The arguments to pass.
+ python: The python interpreter to use.
+ """
+ env = os.environ.copy()
+ env['PYTHON'] = python
+ env['PATH'] = os.environ['PATH'] + os.pathsep + os.path.dirname(python)
+ subprocess.run(
+ [sys.executable, '-m', 'tox', '-vv', '-e', toxenv] + list(args),
+ env=env, check=True)
+
+
+def run_asciidoc2html(args):
+ """Common buildsteps used for all OS'."""
+ utils.print_title("Running asciidoc2html.py")
+ if args.asciidoc is not None:
+ a2h_args = ['--asciidoc'] + args.asciidoc
+ else:
+ a2h_args = []
+ call_script('asciidoc2html.py', *a2h_args)
+
+
+def _maybe_remove(path):
+ """Remove a path if it exists."""
+ try:
+ shutil.rmtree(path)
+ except FileNotFoundError:
+ pass
+
+
+def smoke_test(executable):
+ """Try starting the given qutebrowser executable."""
+ subprocess.run([executable, '--no-err-windows', '--nowindow',
+ '--temp-basedir', 'about:blank', ':later 500 quit'],
+ check=True)
+
+
+def patch_mac_app():
+ """Patch .app to copy missing data and link some libs.
+
+ See https://github.com/pyinstaller/pyinstaller/issues/2276
+ """
+ app_path = os.path.join('dist', 'qutebrowser.app')
+ qtwe_core_dir = os.path.join('.tox', 'pyinstaller', 'lib', 'python3.6',
+ 'site-packages', 'PyQt5', 'Qt', 'lib',
+ 'QtWebEngineCore.framework')
+ # Copy QtWebEngineProcess.app
+ proc_app = 'QtWebEngineProcess.app'
+ shutil.copytree(os.path.join(qtwe_core_dir, 'Helpers', proc_app),
+ os.path.join(app_path, 'Contents', 'MacOS', proc_app))
+ # Copy resources
+ for f in glob.glob(os.path.join(qtwe_core_dir, 'Resources', '*')):
+ dest = os.path.join(app_path, 'Contents', 'Resources')
+ if os.path.isdir(f):
+ dir_dest = os.path.join(dest, os.path.basename(f))
+ print("Copying directory {} to {}".format(f, dir_dest))
+ shutil.copytree(f, dir_dest)
+ else:
+ print("Copying {} to {}".format(f, dest))
+ shutil.copy(f, dest)
+ # Link dependencies
+ for lib in ['QtCore', 'QtWebEngineCore', 'QtQuick', 'QtQml', 'QtNetwork',
+ 'QtGui', 'QtWebChannel', 'QtPositioning']:
+ dest = os.path.join(app_path, lib + '.framework', 'Versions', '5')
+ os.makedirs(dest)
+ os.symlink(os.path.join(os.pardir, os.pardir, os.pardir, 'Contents',
+ 'MacOS', lib),
+ os.path.join(dest, lib))
+ # Patch Info.plist - pyinstaller's options are too limiting
+ plist_path = os.path.join(app_path, 'Contents', 'Info.plist')
+ with open(plist_path, "rb") as f:
+ plist_data = plistlib.load(f)
+ plist_data.update(INFO_PLIST_UPDATES)
+ with open(plist_path, "wb") as f:
+ plistlib.dump(plist_data, f)
+
+
+INFO_PLIST_UPDATES = {
+ 'CFBundleVersion': qutebrowser.__version__,
+ 'CFBundleShortVersionString': qutebrowser.__version__,
+ 'NSSupportsAutomaticGraphicsSwitching': True,
+ 'NSHighResolutionCapable': True,
+ 'CFBundleURLTypes': [{
+ "CFBundleURLName": "http(s) URL",
+ "CFBundleURLSchemes": ["http", "https"]
+ }, {
+ "CFBundleURLName": "local file URL",
+ "CFBundleURLSchemes": ["file"]
+ }],
+ 'CFBundleDocumentTypes': [{
+ "CFBundleTypeExtensions": ["html", "htm"],
+ "CFBundleTypeMIMETypes": ["text/html"],
+ "CFBundleTypeName": "HTML document",
+ "CFBundleTypeOSTypes": ["HTML"],
+ "CFBundleTypeRole": "Viewer",
+ }, {
+ "CFBundleTypeExtensions": ["xhtml"],
+ "CFBundleTypeMIMETypes": ["text/xhtml"],
+ "CFBundleTypeName": "XHTML document",
+ "CFBundleTypeRole": "Viewer",
+ }]
+}
+
+
+def build_mac():
+ """Build macOS .dmg/.app."""
+ utils.print_title("Cleaning up...")
+ for f in ['wc.dmg', 'template.dmg']:
+ try:
+ os.remove(f)
+ except FileNotFoundError:
+ pass
+ for d in ['dist', 'build']:
+ shutil.rmtree(d, ignore_errors=True)
+ utils.print_title("Updating 3rdparty content")
+ # Currently disabled because QtWebEngine has no pdfjs support
+ # update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False)
+ utils.print_title("Building .app via pyinstaller")
+ call_tox('pyinstaller', '-r')
+ utils.print_title("Patching .app")
+ patch_mac_app()
+ utils.print_title("Building .dmg")
+ subprocess.run(['make', '-f', 'scripts/dev/Makefile-dmg'], check=True)
+
+ dmg_name = 'qutebrowser-{}.dmg'.format(qutebrowser.__version__)
+ os.rename('qutebrowser.dmg', dmg_name)
+
+ utils.print_title("Running smoke test")
+
+ try:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ subprocess.run(['hdiutil', 'attach', dmg_name,
+ '-mountpoint', tmpdir], check=True)
+ try:
+ binary = os.path.join(tmpdir, 'qutebrowser.app', 'Contents',
+ 'MacOS', 'qutebrowser')
+ smoke_test(binary)
+ finally:
+ time.sleep(5)
+ subprocess.run(['hdiutil', 'detach', tmpdir])
+ except PermissionError as e:
+ print("Failed to remove tempdir: {}".format(e))
+
+ return [(dmg_name, 'application/x-apple-diskimage', 'macOS .dmg')]
+
+
+def patch_windows(out_dir):
+ """Copy missing DLLs for windows into the given output."""
+ dll_dir = os.path.join('.tox', 'pyinstaller', 'lib', 'site-packages',
+ 'PyQt5', 'Qt', 'bin')
+ dlls = ['libEGL.dll', 'libGLESv2.dll', 'libeay32.dll', 'ssleay32.dll']
+ for dll in dlls:
+ shutil.copy(os.path.join(dll_dir, dll), out_dir)
+
+
+def build_windows():
+ """Build windows executables/setups."""
+ utils.print_title("Updating 3rdparty content")
+ # Currently disabled because QtWebEngine has no pdfjs support
+ # update_3rdparty.run(ace=False, pdfjs=True, fancy_dmg=False)
+
+ utils.print_title("Building Windows binaries")
+ parts = str(sys.version_info.major), str(sys.version_info.minor)
+ ver = ''.join(parts)
+ dot_ver = '.'.join(parts)
+
+ # Get python path from registry if possible
+ try:
+ reg64_key = winreg.OpenKeyEx(winreg.HKEY_LOCAL_MACHINE,
+ r'SOFTWARE\Python\PythonCore'
+ r'\{}\InstallPath'.format(dot_ver))
+ python_x64 = winreg.QueryValueEx(reg64_key, 'ExecutablePath')[0]
+ except FileNotFoundError:
+ python_x64 = r'C:\Python{}\python.exe'.format(ver)
+
+ out_pyinstaller = os.path.join('dist', 'qutebrowser')
+ out_64 = os.path.join('dist',
+ 'qutebrowser-{}-x64'.format(qutebrowser.__version__))
+
+ artifacts = []
+
+ from scripts.dev import gen_versioninfo
+ utils.print_title("Updating VersionInfo file")
+ gen_versioninfo.main()
+
+ utils.print_title("Running pyinstaller 64bit")
+ _maybe_remove(out_64)
+ call_tox('pyinstaller', '-r', python=python_x64)
+ shutil.move(out_pyinstaller, out_64)
+ patch_windows(out_64)
+
+ utils.print_title("Building installers")
+ subprocess.run(['makensis.exe',
+ '/DX64',
+ '/DVERSION={}'.format(qutebrowser.__version__),
+ 'misc/qutebrowser.nsi'], check=True)
+
+ name_64 = 'qutebrowser-{}-amd64.exe'.format(qutebrowser.__version__)
+
+ artifacts += [
+ (os.path.join('dist', name_64),
+ 'application/vnd.microsoft.portable-executable',
+ 'Windows 64bit installer'),
+ ]
+
+ utils.print_title("Running 64bit smoke test")
+ smoke_test(os.path.join(out_64, 'qutebrowser.exe'))
+
+ utils.print_title("Zipping 64bit standalone...")
+ name = 'qutebrowser-{}-windows-standalone-amd64'.format(
+ qutebrowser.__version__)
+ shutil.make_archive(name, 'zip', 'dist', os.path.basename(out_64))
+ artifacts.append(('{}.zip'.format(name),
+ 'application/zip',
+ 'Windows 64bit standalone'))
+
+ return artifacts
+
+
+def build_sdist():
+ """Build an sdist and list the contents."""
+ utils.print_title("Building sdist")
+
+ _maybe_remove('dist')
+
+ subprocess.run([sys.executable, 'setup.py', 'sdist'], check=True)
+ dist_files = os.listdir(os.path.abspath('dist'))
+ assert len(dist_files) == 1
+
+ dist_file = os.path.join('dist', dist_files[0])
+ subprocess.run(['gpg', '--detach-sign', '-a', 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)
+
+ assert '.pyc' not in by_ext
+
+ utils.print_title("sdist contents")
+
+ for ext, files in sorted(by_ext.items()):
+ 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'),
+ ]
+
+ return artifacts
+
+
+def test_makefile():
+ """Make sure the Makefile works correctly."""
+ utils.print_title("Testing makefile")
+ with tempfile.TemporaryDirectory() as tmpdir:
+ subprocess.run(['make', '-f', 'misc/Makefile',
+ 'DESTDIR={}'.format(tmpdir), 'install'], check=True)
+
+
+def read_github_token():
+ """Read the GitHub API token from disk."""
+ token_file = os.path.join(os.path.expanduser('~'), '.gh_token')
+ with open(token_file, encoding='ascii') as f:
+ token = f.read().strip()
+ return token
+
+
+def github_upload(artifacts, tag):
+ """Upload the given artifacts to GitHub.
+
+ Args:
+ artifacts: A list of (filename, mimetype, description) tuples
+ tag: The name of the release tag
+ """
+ import github3
+ utils.print_title("Uploading to github...")
+
+ token = read_github_token()
+ gh = github3.login(token=token)
+ repo = gh.repository('qutebrowser', 'qutebrowser')
+
+ release = None # to satisfy pylint
+ for release in repo.releases():
+ if release.tag_name == tag:
+ break
+ else:
+ raise Exception("No release found for {!r}!".format(tag))
+
+ for filename, mimetype, description in artifacts:
+ with open(filename, 'rb') as f:
+ basename = os.path.basename(filename)
+ asset = release.upload_asset(mimetype, basename, f)
+ asset.edit(basename, description)
+
+
+def pypi_upload(artifacts):
+ """Upload the given artifacts to PyPI using twine."""
+ filenames = [a[0] for a in artifacts]
+ subprocess.run(['twine', 'upload'] + filenames, check=True)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--asciidoc', help="Full path to python and "
+ "asciidoc.py. If not given, it's searched in PATH.",
+ nargs=2, required=False,
+ metavar=('PYTHON', 'ASCIIDOC'))
+ parser.add_argument('--upload', help="Tag to upload the release for",
+ nargs=1, required=False, metavar='TAG')
+ args = parser.parse_args()
+ utils.change_cwd()
+
+ upload_to_pypi = False
+
+ if args.upload is not None:
+ # Fail early when trying to upload without github3 installed
+ # or without API token
+ import github3 # pylint: disable=unused-variable
+ read_github_token()
+
+ run_asciidoc2html(args)
+ if os.name == 'nt':
+ artifacts = build_windows()
+ elif sys.platform == 'darwin':
+ artifacts = build_mac()
+ else:
+ test_makefile()
+ artifacts = build_sdist()
+ upload_to_pypi = True
+
+ if args.upload is not None:
+ utils.print_title("Press enter to release...")
+ input()
+ github_upload(artifacts, args.upload[0])
+ if upload_to_pypi:
+ pypi_upload(artifacts)
+ else:
+ print()
+ utils.print_title("Artifacts")
+ for artifact in artifacts:
+ print(artifact)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/check_coverage.py b/.config/qutebrowser/scripts/dev/check_coverage.py
new file mode 100644
index 0000000..32c5afc
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/check_coverage.py
@@ -0,0 +1,348 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2015-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Enforce perfect coverage on some files."""
+
+import os
+import os.path
+import sys
+import enum
+import subprocess
+from xml.etree import ElementTree
+
+import attr
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+from scripts import utils as scriptutils
+from qutebrowser.utils import utils
+
+
+@attr.s
+class Message:
+
+ """A message shown by coverage.py."""
+
+ typ = attr.ib()
+ filename = attr.ib()
+ text = attr.ib()
+
+
+MsgType = enum.Enum('MsgType', 'insufficent_coverage, perfect_file')
+
+
+# A list of (test_file, tested_file) tuples. test_file can be None.
+PERFECT_FILES = [
+ (None,
+ 'commands/cmdexc.py'),
+ ('tests/unit/commands/test_cmdutils.py',
+ 'commands/cmdutils.py'),
+ ('tests/unit/commands/test_argparser.py',
+ 'commands/argparser.py'),
+
+ ('tests/unit/browser/webkit/test_cache.py',
+ 'browser/webkit/cache.py'),
+ ('tests/unit/browser/webkit/test_cookies.py',
+ 'browser/webkit/cookies.py'),
+ ('tests/unit/browser/test_history.py',
+ 'browser/history.py'),
+ ('tests/unit/browser/webkit/http/test_http.py',
+ 'browser/webkit/http.py'),
+ ('tests/unit/browser/webkit/http/test_content_disposition.py',
+ 'browser/webkit/rfc6266.py'),
+ # ('tests/unit/browser/webkit/test_webkitelem.py',
+ # 'browser/webkit/webkitelem.py'),
+ # ('tests/unit/browser/webkit/test_webkitelem.py',
+ # 'browser/webelem.py'),
+ ('tests/unit/browser/webkit/network/test_filescheme.py',
+ 'browser/webkit/network/filescheme.py'),
+ ('tests/unit/browser/webkit/network/test_networkreply.py',
+ 'browser/webkit/network/networkreply.py'),
+
+ ('tests/unit/browser/test_signalfilter.py',
+ 'browser/signalfilter.py'),
+ (None,
+ 'browser/webengine/certificateerror.py'),
+ # ('tests/unit/browser/test_tab.py',
+ # 'browser/tab.py'),
+
+ ('tests/unit/keyinput/test_basekeyparser.py',
+ 'keyinput/basekeyparser.py'),
+ ('tests/unit/keyinput/test_keyutils.py',
+ 'keyinput/keyutils.py'),
+
+ ('tests/unit/misc/test_autoupdate.py',
+ 'misc/autoupdate.py'),
+ ('tests/unit/misc/test_readline.py',
+ 'misc/readline.py'),
+ ('tests/unit/misc/test_split.py',
+ 'misc/split.py'),
+ ('tests/unit/misc/test_msgbox.py',
+ 'misc/msgbox.py'),
+ ('tests/unit/misc/test_checkpyver.py',
+ 'misc/checkpyver.py'),
+ ('tests/unit/misc/test_guiprocess.py',
+ 'misc/guiprocess.py'),
+ ('tests/unit/misc/test_editor.py',
+ 'misc/editor.py'),
+ ('tests/unit/misc/test_cmdhistory.py',
+ 'misc/cmdhistory.py'),
+ ('tests/unit/misc/test_ipc.py',
+ 'misc/ipc.py'),
+ ('tests/unit/misc/test_keyhints.py',
+ 'misc/keyhintwidget.py'),
+ ('tests/unit/misc/test_pastebin.py',
+ 'misc/pastebin.py'),
+ (None,
+ 'misc/objects.py'),
+
+ (None,
+ 'mainwindow/statusbar/keystring.py'),
+ ('tests/unit/mainwindow/statusbar/test_percentage.py',
+ 'mainwindow/statusbar/percentage.py'),
+ ('tests/unit/mainwindow/statusbar/test_progress.py',
+ 'mainwindow/statusbar/progress.py'),
+ ('tests/unit/mainwindow/statusbar/test_tabindex.py',
+ 'mainwindow/statusbar/tabindex.py'),
+ ('tests/unit/mainwindow/statusbar/test_textbase.py',
+ 'mainwindow/statusbar/textbase.py'),
+ ('tests/unit/mainwindow/statusbar/test_url.py',
+ 'mainwindow/statusbar/url.py'),
+ ('tests/unit/mainwindow/statusbar/test_backforward.py',
+ 'mainwindow/statusbar/backforward.py'),
+ ('tests/unit/mainwindow/test_messageview.py',
+ 'mainwindow/messageview.py'),
+
+ ('tests/unit/config/test_config.py',
+ 'config/config.py'),
+ ('tests/unit/config/test_configdata.py',
+ 'config/configdata.py'),
+ ('tests/unit/config/test_configexc.py',
+ 'config/configexc.py'),
+ ('tests/unit/config/test_configfiles.py',
+ 'config/configfiles.py'),
+ ('tests/unit/config/test_configtypes.py',
+ 'config/configtypes.py'),
+ ('tests/unit/config/test_configinit.py',
+ 'config/configinit.py'),
+ ('tests/unit/config/test_configcommands.py',
+ 'config/configcommands.py'),
+ ('tests/unit/config/test_configutils.py',
+ 'config/configutils.py'),
+
+ ('tests/unit/utils/test_qtutils.py',
+ 'utils/qtutils.py'),
+ ('tests/unit/utils/test_standarddir.py',
+ 'utils/standarddir.py'),
+ ('tests/unit/utils/test_urlutils.py',
+ 'utils/urlutils.py'),
+ ('tests/unit/utils/usertypes',
+ 'utils/usertypes.py'),
+ ('tests/unit/utils/test_utils.py',
+ 'utils/utils.py'),
+ ('tests/unit/utils/test_version.py',
+ 'utils/version.py'),
+ ('tests/unit/utils/test_debug.py',
+ 'utils/debug.py'),
+ ('tests/unit/utils/test_jinja.py',
+ 'utils/jinja.py'),
+ ('tests/unit/utils/test_error.py',
+ 'utils/error.py'),
+ ('tests/unit/utils/test_javascript.py',
+ 'utils/javascript.py'),
+ ('tests/unit/utils/test_urlmatch.py',
+ 'utils/urlmatch.py'),
+
+ (None,
+ 'completion/models/util.py'),
+ ('tests/unit/completion/test_models.py',
+ 'completion/models/urlmodel.py'),
+ ('tests/unit/completion/test_models.py',
+ 'completion/models/configmodel.py'),
+ ('tests/unit/completion/test_histcategory.py',
+ 'completion/models/histcategory.py'),
+ ('tests/unit/completion/test_listcategory.py',
+ 'completion/models/listcategory.py'),
+
+ ('tests/unit/browser/webengine/test_spell.py',
+ 'browser/webengine/spell.py'),
+
+]
+
+
+# 100% coverage because of end2end tests, but no perfect unit tests yet.
+WHITELISTED_FILES = [
+ 'browser/webkit/webkitinspector.py',
+ 'keyinput/macros.py',
+ 'browser/webkit/webkitelem.py',
+]
+
+
+class Skipped(Exception):
+
+ """Exception raised when skipping coverage checks."""
+
+ def __init__(self, reason):
+ self.reason = reason
+ super().__init__("Skipping coverage checks " + reason)
+
+
+def _get_filename(filename):
+ """Transform the absolute test filenames to relative ones."""
+ if os.path.isabs(filename):
+ basedir = os.path.abspath(
+ os.path.join(os.path.dirname(__file__), '..', '..'))
+ common_path = os.path.commonprefix([basedir, filename])
+ if common_path:
+ filename = filename[len(common_path):].lstrip('/')
+ if filename.startswith('qutebrowser/'):
+ filename = filename.split('/', maxsplit=1)[1]
+
+ return filename
+
+
+def check(fileobj, perfect_files):
+ """Main entry point which parses/checks coverage.xml if applicable."""
+ if not utils.is_linux:
+ raise Skipped("on non-Linux system.")
+ elif '-k' in sys.argv[1:]:
+ raise Skipped("because -k is given.")
+ elif '-m' in sys.argv[1:]:
+ raise Skipped("because -m is given.")
+ elif '--lf' in sys.argv[1:]:
+ raise Skipped("because --lf is given.")
+
+ perfect_src_files = [e[1] for e in perfect_files]
+
+ filename_args = [arg for arg in sys.argv[1:]
+ if arg.startswith('tests' + os.sep)]
+ filtered_files = [tpl[1] for tpl in perfect_files if tpl[0] in
+ filename_args]
+
+ if filename_args and not filtered_files:
+ raise Skipped("because there is nothing to check.")
+
+ tree = ElementTree.parse(fileobj)
+ classes = tree.getroot().findall('./packages/package/classes/class')
+
+ messages = []
+
+ for klass in classes:
+ filename = _get_filename(klass.attrib['filename'])
+
+ line_cov = float(klass.attrib['line-rate']) * 100
+ branch_cov = float(klass.attrib['branch-rate']) * 100
+
+ if filtered_files and filename not in filtered_files:
+ continue
+
+ assert 0 <= line_cov <= 100, line_cov
+ assert 0 <= branch_cov <= 100, branch_cov
+ assert '\\' not in filename, filename
+
+ is_bad = line_cov < 100 or branch_cov < 100
+
+ if filename in perfect_src_files and is_bad:
+ text = "{} has {:.2f}% line and {:.2f}% branch coverage!".format(
+ filename, line_cov, branch_cov)
+ messages.append(Message(MsgType.insufficent_coverage, filename,
+ text))
+ elif (filename not in perfect_src_files and not is_bad and
+ filename not in WHITELISTED_FILES):
+ text = ("{} has 100% coverage but is not in "
+ "perfect_files!".format(filename))
+ messages.append(Message(MsgType.perfect_file, filename, text))
+
+ return messages
+
+
+def main_check():
+ """Check coverage after a test run."""
+ try:
+ with open('coverage.xml', encoding='utf-8') as f:
+ messages = check(f, PERFECT_FILES)
+ except Skipped as e:
+ print(e)
+ messages = []
+
+ if messages:
+ print()
+ print()
+ scriptutils.print_title("Coverage check failed")
+ for msg in messages:
+ print(msg.text)
+ print()
+ filters = ','.join('qutebrowser/' + msg.filename for msg in messages)
+ subprocess.run([sys.executable, '-m', 'coverage', 'report',
+ '--show-missing', '--include', filters], check=True)
+ print()
+ print("To debug this, run 'tox -e py36-pyqt59-cov' "
+ "(or py35-pyqt59-cov) locally and check htmlcov/index.html")
+ print("or check https://codecov.io/github/qutebrowser/qutebrowser")
+ print()
+
+ if 'CI' in os.environ:
+ print("Keeping coverage.xml on CI.")
+ else:
+ os.remove('coverage.xml')
+ return 1 if messages else 0
+
+
+def main_check_all():
+ """Check the coverage for all files individually.
+
+ This makes sure the files have 100% coverage without running unrelated
+ tests.
+
+ This runs pytest with the used executable, so check_coverage.py should be
+ called with something like ./.tox/py36/bin/python.
+ """
+ for test_file, src_file in PERFECT_FILES:
+ if test_file is None:
+ continue
+ subprocess.run(
+ [sys.executable, '-m', 'pytest', '--cov', 'qutebrowser',
+ '--cov-report', 'xml', test_file], check=True)
+ with open('coverage.xml', encoding='utf-8') as f:
+ messages = check(f, [(test_file, src_file)])
+ os.remove('coverage.xml')
+
+ messages = [msg for msg in messages
+ if msg.typ == MsgType.insufficent_coverage]
+ if messages:
+ for msg in messages:
+ print(msg.text)
+ return 1
+ else:
+ print("Check ok!")
+ return 0
+
+
+def main():
+ scriptutils.change_cwd()
+ if '--check-all' in sys.argv:
+ return main_check_all()
+ else:
+ return main_check()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/.config/qutebrowser/scripts/dev/check_doc_changes.py b/.config/qutebrowser/scripts/dev/check_doc_changes.py
new file mode 100755
index 0000000..3d90bea
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/check_doc_changes.py
@@ -0,0 +1,48 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2016-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Check if docs changed and output an error if so."""
+
+import sys
+import subprocess
+import os
+
+code = subprocess.run(['git', '--no-pager', 'diff',
+ '--exit-code', '--stat']).returncode
+
+if os.environ.get('TRAVIS_PULL_REQUEST', 'false') != 'false':
+ if code != 0:
+ print("Docs changed but ignoring change as we're building a PR")
+ sys.exit(0)
+
+if code != 0:
+ print()
+ print('The autogenerated docs changed, please run this to update them:')
+ print(' tox -e docs')
+ print(' git commit -am "Update docs"')
+ print()
+ print('(Or you have uncommitted changes, in which case you can ignore '
+ 'this.)')
+ if 'TRAVIS' in os.environ:
+ print()
+ print("travis_fold:start:gitdiff")
+ subprocess.run(['git', '--no-pager', 'diff'])
+ print("travis_fold:end:gitdiff")
+sys.exit(code)
diff --git a/.config/qutebrowser/scripts/dev/ci/travis_backtrace.sh b/.config/qutebrowser/scripts/dev/ci/travis_backtrace.sh
new file mode 100644
index 0000000..227dde8
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/ci/travis_backtrace.sh
@@ -0,0 +1,18 @@
+#!/bin/bash
+#
+# Find all possible core files under current directory. Attempt
+# to determine exe using file(1) and dump stack trace with gdb.
+#
+
+case $TESTENV in
+ py3*-pyqt*)
+ exe=$(readlink -f ".tox/$TESTENV/bin/python")
+ full=
+ ;;
+ *)
+ echo "Skipping coredump analysis in testenv $TESTENV!"
+ exit 0
+ ;;
+esac
+
+find . \( -name "*.core" -o -name core \) -exec gdb --batch --quiet -ex "thread apply all bt $full" "$exe" {} \;
diff --git a/.config/qutebrowser/scripts/dev/ci/travis_install.sh b/.config/qutebrowser/scripts/dev/ci/travis_install.sh
new file mode 100644
index 0000000..18f5aa9
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/ci/travis_install.sh
@@ -0,0 +1,108 @@
+#!/bin/bash
+# vim: ft=sh fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2016-2017 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 <http://www.gnu.org/licenses/>.
+
+# Stolen from https://github.com/travis-ci/travis-build/blob/master/lib/travis/build/templates/header.sh
+# and adjusted to use ((...))
+travis_retry() {
+ local ANSI_RED='\033[31;1m'
+ local ANSI_RESET='\033[0m'
+ local result=0
+ local count=1
+ while (( count < 3 )); do
+ if (( result != 0 )); then
+ echo -e "\\n${ANSI_RED}The command \"$*\" failed. Retrying, $count of 3.${ANSI_RESET}\\n" >&2
+ fi
+ "$@"
+ result=$?
+ (( result == 0 )) && break
+ count=$(( count + 1 ))
+ sleep 1
+ done
+
+ if (( count > 3 )); then
+ echo -e "\\n${ANSI_RED}The command \"$*\" failed 3 times.${ANSI_RESET}\\n" >&2
+ fi
+
+ return $result
+}
+
+pip_install() {
+ travis_retry python3 -m pip install "$@"
+}
+
+npm_install() {
+ # Make sure npm is up-to-date first
+ travis_retry npm install -g npm
+ travis_retry npm install -g "$@"
+}
+
+check_pyqt() {
+ python3 <<EOF
+import sys
+from PyQt5.QtCore import QT_VERSION_STR, PYQT_VERSION_STR, qVersion
+try:
+ from PyQt.sip import SIP_VERSION_STR
+except ModuleNotFoundError:
+ from sip import SIP_VERSION_STR
+
+print("Python {}".format(sys.version))
+print("PyQt5 {}".format(PYQT_VERSION_STR))
+print("Qt5 {} (runtime {})".format(QT_VERSION_STR, qVersion()))
+print("sip {}".format(SIP_VERSION_STR))
+EOF
+}
+
+set -e
+
+if [[ $DOCKER ]]; then
+ exit 0
+elif [[ $TRAVIS_OS_NAME == osx ]]; then
+ # Disable App Nap
+ defaults write NSGlobalDomain NSAppSleepDisabled -bool YES
+
+ curl -LO https://bootstrap.pypa.io/get-pip.py
+ sudo -H python get-pip.py
+
+ brew --version
+ brew update
+ brew upgrade python libyaml
+ brew install qt5 pyqt5
+
+ pip_install -r misc/requirements/requirements-tox.txt
+ python3 -m pip --version
+ tox --version
+ check_pyqt
+ exit 0
+fi
+
+case $TESTENV in
+ eslint)
+ npm_install eslint
+ ;;
+ shellcheck)
+ ;;
+ *)
+ pip_install pip
+ pip_install -r misc/requirements/requirements-tox.txt
+ if [[ $TESTENV == *-cov ]]; then
+ pip_install -r misc/requirements/requirements-codecov.txt
+ fi
+ ;;
+esac
diff --git a/.config/qutebrowser/scripts/dev/ci/travis_run.sh b/.config/qutebrowser/scripts/dev/ci/travis_run.sh
new file mode 100644
index 0000000..55ca7c1
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/ci/travis_run.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+if [[ $DOCKER ]]; then
+ docker run \
+ --privileged \
+ -v "$PWD:/outside" \
+ -e "QUTE_BDD_WEBENGINE=$QUTE_BDD_WEBENGINE" \
+ -e "DOCKER=$DOCKER" \
+ -e "CI=$CI" \
+ -e "TRAVIS=$TRAVIS" \
+ "qutebrowser/travis:$DOCKER"
+elif [[ $TESTENV == eslint ]]; then
+ # Can't run this via tox as we can't easily install tox in the javascript
+ # travis env
+ cd qutebrowser/javascript || exit 1
+ eslint --color --report-unused-disable-directives .
+elif [[ $TESTENV == shellcheck ]]; then
+ SCRIPTS=$( mktemp )
+ find scripts/dev/ -name '*.sh' >"$SCRIPTS"
+ find misc/userscripts/ -type f -exec grep -lE '[/ ][bd]ash$|[/ ]sh$|[/ ]ksh$' {} + >>"$SCRIPTS"
+ mapfile -t scripts <"$SCRIPTS"
+ rm -f "$SCRIPTS"
+ docker run \
+ -v "$PWD:/outside" \
+ -w /outside \
+ koalaman/shellcheck:latest "${scripts[@]}"
+else
+ args=()
+ [[ $TRAVIS_OS_NAME == osx ]] && args=('--qute-bdd-webengine' '--no-xvfb' 'tests/unit')
+
+ tox -e "$TESTENV" -- "${args[@]}"
+fi
diff --git a/.config/qutebrowser/scripts/dev/cleanup.py b/.config/qutebrowser/scripts/dev/cleanup.py
new file mode 100755
index 0000000..d1bb84a
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/cleanup.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Script to clean up the mess made by Python/setuptools/PyInstaller."""
+
+import os
+import os.path
+import sys
+import glob
+import shutil
+import fnmatch
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+from scripts import utils
+
+
+recursive_lint = ('__pycache__', '*.pyc')
+lint = ('build', 'dist', 'pkg/pkg', 'pkg/qutebrowser-*.pkg.tar.xz', 'pkg/src',
+ 'pkg/qutebrowser', 'qutebrowser.egg-info', 'setuptools-*.egg',
+ 'setuptools-*.zip', 'doc/qutebrowser.asciidoc', 'doc/*.html',
+ 'doc/qutebrowser.1', 'README.html', 'qutebrowser/html/doc')
+
+
+def remove(path):
+ """Remove either a file or directory unless --dry-run is given."""
+ if os.path.isdir(path):
+ print("rm -r '{}'".format(path))
+ if '--dry-run' not in sys.argv:
+ shutil.rmtree(path)
+ else:
+ print("rm '{}'".format(path))
+ if '--dry-run' not in sys.argv:
+ os.remove(path)
+
+
+def main():
+ """Clean up lint in the current dir."""
+ utils.change_cwd()
+ for elem in lint:
+ for f in glob.glob(elem):
+ remove(f)
+
+ for root, _dirs, _files in os.walk(os.getcwd()):
+ path = os.path.basename(root)
+ if any(fnmatch.fnmatch(path, e) for e in recursive_lint):
+ remove(root)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/download_release.sh b/.config/qutebrowser/scripts/dev/download_release.sh
new file mode 100644
index 0000000..207da21
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/download_release.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+set -e
+
+# This script downloads the given release from GitHub so we can mirror it on
+# qutebrowser.org.
+
+tmpdir=$(mktemp -d)
+oldpwd=$PWD
+
+if [[ $# != 1 ]]; then
+ echo "Usage: $0 <version>" >&2
+ exit 1
+fi
+
+cd "$tmpdir"
+mkdir windows
+
+base="https://github.com/qutebrowser/qutebrowser/releases/download/v$1"
+
+wget "$base/qutebrowser-$1.tar.gz"
+wget "$base/qutebrowser-$1.tar.gz.asc"
+wget "$base/qutebrowser-$1.dmg"
+wget "$base/qutebrowser_${1}-1_all.deb"
+
+cd windows
+wget "$base/qutebrowser-${1}-amd64.msi"
+wget "$base/qutebrowser-${1}-win32.msi"
+wget "$base/qutebrowser-${1}-windows-standalone-amd64.zip"
+wget "$base/qutebrowser-${1}-windows-standalone-win32.zip"
+
+dest="/srv/http/qutebrowser/releases/v$1"
+cd "$oldpwd"
+sudo mv "$tmpdir" "$dest"
+sudo chown -R http:http "$dest"
diff --git a/.config/qutebrowser/scripts/dev/gen_resources.py b/.config/qutebrowser/scripts/dev/gen_resources.py
new file mode 100644
index 0000000..cbfc69b
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/gen_resources.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# copyright 2014 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 <http://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/.config/qutebrowser/scripts/dev/gen_versioninfo.py b/.config/qutebrowser/scripts/dev/gen_versioninfo.py
new file mode 100644
index 0000000..1aa4b64
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/gen_versioninfo.py
@@ -0,0 +1,85 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2018 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 <http://www.gnu.org/licenses/>.
+
+"""Generate file_version_info.txt for Pyinstaller use with Windows builds."""
+
+import os.path
+import sys
+
+# pylint: disable=import-error,no-member,useless-suppression
+from PyInstaller.utils.win32 import versioninfo as vs
+# pylint: enable=import-error,no-member,useless-suppression
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+import qutebrowser
+from scripts import utils
+
+
+def main():
+ utils.change_cwd()
+ out_filename = 'misc/file_version_info.txt'
+
+ filevers = qutebrowser.__version_info__ + (0,)
+ prodvers = qutebrowser.__version_info__ + (0,)
+ str_filevers = qutebrowser.__version__
+ str_prodvers = qutebrowser.__version__
+
+ comment_text = qutebrowser.__doc__
+ copyright_text = qutebrowser.__copyright__
+ trademark_text = ("qutebrowser is free software under the GNU General "
+ "Public License")
+
+ # https://www.science.co.il/language/Locale-codes.php#definitions
+ # https://msdn.microsoft.com/en-us/library/windows/desktop/dd317756.aspx
+ en_us = 1033 # 0x0409
+ utf_16 = 1200 # 0x04B0
+
+ ffi = vs.FixedFileInfo(filevers, prodvers)
+
+ kids = [
+ vs.StringFileInfo([
+ # 0x0409: MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US)
+ # 0x04B0: codepage 1200 (UTF-16LE)
+ vs.StringTable('040904B0', [
+ vs.StringStruct('Comments', comment_text),
+ vs.StringStruct('CompanyName', "qutebrowser.org"),
+ vs.StringStruct('FileDescription', "qutebrowser"),
+ vs.StringStruct('FileVersion', str_filevers),
+ vs.StringStruct('InternalName', "qutebrowser"),
+ vs.StringStruct('LegalCopyright', copyright_text),
+ vs.StringStruct('LegalTrademarks', trademark_text),
+ vs.StringStruct('OriginalFilename', "qutebrowser.exe"),
+ vs.StringStruct('ProductName', "qutebrowser"),
+ vs.StringStruct('ProductVersion', str_prodvers)
+ ]),
+ ]),
+ vs.VarFileInfo([vs.VarStruct('Translation', [en_us, utf_16])]),
+ ]
+
+ file_version_info = vs.VSVersionInfo(ffi, kids)
+
+ with open(out_filename, 'w', encoding='utf-8') as f:
+ f.write(str(file_version_info))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/get_coredumpctl_traces.py b/.config/qutebrowser/scripts/dev/get_coredumpctl_traces.py
new file mode 100644
index 0000000..d286d38
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/get_coredumpctl_traces.py
@@ -0,0 +1,176 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2015-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Get qutebrowser crash information and stacktraces from coredumpctl."""
+
+import os
+import os.path
+import sys
+import argparse
+import subprocess
+import tempfile
+
+import attr
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+from scripts import utils
+
+
+@attr.s
+class Line:
+
+ """A line in "coredumpctl list"."""
+
+ time = attr.ib()
+ pid = attr.ib()
+ uid = attr.ib()
+ gid = attr.ib()
+ sig = attr.ib()
+ present = attr.ib()
+ exe = attr.ib()
+
+
+def _convert_present(data):
+ """Convert " "/"*" to True/False for parse_coredumpctl_line."""
+ if data == '*':
+ return True
+ elif data == ' ':
+ return False
+ else:
+ raise ValueError(data)
+
+
+def parse_coredumpctl_line(line):
+ """Parse a given string coming from coredumpctl and return a Line object.
+
+ Example input:
+ Mon 2015-09-28 23:22:24 CEST 10606 1000 1000 11 /usr/bin/python3.4
+ """
+ fields = {
+ 'time': (0, 28, str),
+ 'pid': (29, 35, int),
+ 'uid': (36, 41, int),
+ 'gid': (42, 47, int),
+ 'sig': (48, 51, int),
+ 'present': (52, 53, _convert_present),
+ 'exe': (54, None, str),
+ }
+
+ data = {}
+ for name, (start, end, converter) in fields.items():
+ data[name] = converter(line[start:end])
+ return Line(**data)
+
+
+def get_info(pid):
+ """Get and parse "coredumpctl info" output for the given PID."""
+ data = {}
+ output = subprocess.run(['coredumpctl', 'info', str(pid)], check=True,
+ stdout=subprocess.PIPE).stdout
+ output = output.decode('utf-8')
+ for line in output.split('\n'):
+ if not line.strip():
+ continue
+ try:
+ key, value = line.split(':', maxsplit=1)
+ except ValueError:
+ # systemd stack output
+ continue
+ data[key.strip()] = value.strip()
+ return data
+
+
+def is_qutebrowser_dump(parsed):
+ """Check if the given Line is a qutebrowser dump."""
+ basename = os.path.basename(parsed.exe)
+ if basename == 'python' or basename.startswith('python3'):
+ info = get_info(parsed.pid)
+ try:
+ cmdline = info['Command Line']
+ except KeyError:
+ return True
+ else:
+ return '-m qutebrowser' in cmdline
+ else:
+ return basename == 'qutebrowser'
+
+
+def dump_infos_gdb(parsed):
+ """Dump all needed infos for the given crash using gdb."""
+ with tempfile.TemporaryDirectory() as tempdir:
+ coredump = os.path.join(tempdir, 'dump')
+ subprocess.run(['coredumpctl', 'dump', '-o', coredump,
+ str(parsed.pid)], check=True)
+ subprocess.run(['gdb', parsed.exe, coredump,
+ '-ex', 'info threads',
+ '-ex', 'thread apply all bt full',
+ '-ex', 'quit'], check=True)
+
+
+def dump_infos(parsed):
+ """Dump all possible infos for the given crash."""
+ if not parsed.present:
+ info = get_info(parsed.pid)
+ print("{}: Signal {} with no coredump: {}".format(
+ parsed.time, info.get('Signal', None),
+ info.get('Command Line', None)))
+ else:
+ print('\n\n\n')
+ utils.print_title('{} - {}'.format(parsed.time, parsed.pid))
+ sys.stdout.flush()
+ dump_infos_gdb(parsed)
+
+
+def check_prerequisites():
+ """Check if coredumpctl/gdb are installed."""
+ for binary in ['coredumpctl', 'gdb']:
+ try:
+ subprocess.run([binary, '--version'], check=True)
+ except FileNotFoundError:
+ print("{} is needed to run this script!".format(binary),
+ file=sys.stderr)
+ sys.exit(1)
+
+
+def main():
+ check_prerequisites()
+
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--all', help="Also list crashes without coredumps.",
+ action='store_true')
+ args = parser.parse_args()
+
+ coredumps = subprocess.run(['coredumpctl', 'list'], check=True,
+ stdout=subprocess.PIPE).stdout
+ lines = coredumps.decode('utf-8').split('\n')
+ for line in lines[1:]:
+ if not line.strip():
+ continue
+ parsed = parse_coredumpctl_line(line)
+ if not parsed.present and not args.all:
+ continue
+ if is_qutebrowser_dump(parsed):
+ dump_infos(parsed)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/misc_checks.py b/.config/qutebrowser/scripts/dev/misc_checks.py
new file mode 100644
index 0000000..2992464
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/misc_checks.py
@@ -0,0 +1,163 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Various small code checkers."""
+
+import os
+import os.path
+import re
+import sys
+import argparse
+import subprocess
+import tokenize
+import traceback
+import collections
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+from scripts import utils
+
+
+def _get_files(only_py=False):
+ """Iterate over all python files and yield filenames."""
+ for (dirpath, _dirnames, filenames) in os.walk('.'):
+ parts = dirpath.split(os.sep)
+ if len(parts) >= 2:
+ rootdir = parts[1]
+ if rootdir.startswith('.') or rootdir == 'htmlcov':
+ # ignore hidden dirs and htmlcov
+ continue
+
+ if only_py:
+ endings = {'.py'}
+ else:
+ endings = {'.py', '.asciidoc', '.js', '.feature'}
+ files = (e for e in filenames if os.path.splitext(e)[1] in endings)
+ for name in files:
+ yield os.path.join(dirpath, name)
+
+
+def check_git():
+ """Check for uncommitted git files.."""
+ if not os.path.isdir(".git"):
+ print("No .git dir, ignoring")
+ print()
+ return False
+ untracked = []
+ gitst = subprocess.run(['git', 'status', '--porcelain'], check=True,
+ stdout=subprocess.PIPE).stdout
+ gitst = gitst.decode('UTF-8').strip()
+ for line in gitst.splitlines():
+ s, name = line.split(maxsplit=1)
+ if s == '??' and name != '.venv/':
+ untracked.append(name)
+ status = True
+ if untracked:
+ status = False
+ utils.print_col("Untracked files:", 'red')
+ print('\n'.join(untracked))
+ print()
+ return status
+
+
+def check_spelling():
+ """Check commonly misspelled words."""
+ # Words which I often misspell
+ words = {'[Bb]ehaviour', '[Qq]uitted', 'Ll]ikelyhood', '[Ss]ucessfully',
+ '[Oo]ccur[^rs .]', '[Ss]eperator', '[Ee]xplicitely',
+ '[Aa]uxillary', '[Aa]ccidentaly', '[Aa]mbigious', '[Ll]oosly',
+ '[Ii]nitialis', '[Cc]onvienence', '[Ss]imiliar', '[Uu]ncommited',
+ '[Rr]eproducable', '[Aa]n [Uu]ser', '[Cc]onvienience',
+ '[Ww]ether', '[Pp]rogramatically', '[Ss]plitted', '[Ee]xitted',
+ '[Mm]ininum', '[Rr]esett?ed', '[Rr]ecieved', '[Rr]egularily',
+ '[Uu]nderlaying', '[Ii]nexistant', '[Ee]lipsis', 'commiting',
+ 'existant', '[Rr]esetted', '[Ss]imilarily', '[Ii]nformations'}
+
+ # Words which look better when splitted, but might need some fine tuning.
+ words |= {'[Ww]ebelements', '[Mm]ouseevent', '[Kk]eysequence',
+ '[Nn]ormalmode', '[Ee]ventloops', '[Ss]izehint',
+ '[Ss]tatemachine', '[Mm]etaobject', '[Ll]ogrecord',
+ '[Ff]iletype'}
+
+ # Files which should be ignored, e.g. because they come from another
+ # package
+ ignored = [
+ os.path.join('.', 'scripts', 'dev', 'misc_checks.py'),
+ os.path.join('.', 'qutebrowser', '3rdparty', 'pdfjs'),
+ os.path.join('.', 'tests', 'end2end', 'data', 'hints', 'ace',
+ 'ace.js'),
+ ]
+
+ seen = collections.defaultdict(list)
+ try:
+ ok = True
+ for fn in _get_files():
+ with tokenize.open(fn) as f:
+ if any(fn.startswith(i) for i in ignored):
+ continue
+ for line in f:
+ for w in words:
+ if (re.search(w, line) and
+ fn not in seen[w] and
+ '# pragma: no spellcheck' not in line):
+ print('Found "{}" in {}!'.format(w, fn))
+ seen[w].append(fn)
+ ok = False
+ print()
+ return ok
+ except Exception:
+ traceback.print_exc()
+ return None
+
+
+def check_vcs_conflict():
+ """Check VCS conflict markers."""
+ try:
+ ok = True
+ for fn in _get_files(only_py=True):
+ with tokenize.open(fn) as f:
+ for line in f:
+ if any(line.startswith(c * 7) for c in '<>=|'):
+ print("Found conflict marker in {}".format(fn))
+ ok = False
+ print()
+ return ok
+ except Exception:
+ traceback.print_exc()
+ return None
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('checker', choices=('git', 'vcs', 'spelling'),
+ help="Which checker to run.")
+ args = parser.parse_args()
+ if args.checker == 'git':
+ ok = check_git()
+ elif args.checker == 'vcs':
+ ok = check_vcs_conflict()
+ elif args.checker == 'spelling':
+ ok = check_spelling()
+ return 0 if ok else 1
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/__init__.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/__init__.py
new file mode 100644
index 0000000..1341a93
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/__init__.py
@@ -0,0 +1 @@
+"""Custom pylint checkers."""
diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/config.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/config.py
new file mode 100644
index 0000000..5aa5250
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/config.py
@@ -0,0 +1,84 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Custom astroid checker for config calls."""
+
+import sys
+import pathlib
+
+import yaml
+import astroid
+from pylint import interfaces, checkers
+from pylint.checkers import utils
+
+
+OPTIONS = None
+FAILED_LOAD = False
+
+
+class ConfigChecker(checkers.BaseChecker):
+
+ """Custom astroid checker for config calls."""
+
+ __implements__ = interfaces.IAstroidChecker
+ name = 'config'
+ msgs = {
+ 'E9998': ('%s is no valid config option.', # flake8: disable=S001
+ 'bad-config-option',
+ None),
+ }
+ priority = -1
+ printed_warning = False
+
+ @utils.check_messages('bad-config-option')
+ def visit_attribute(self, node):
+ """Visit a getattr node."""
+ # At the end of a config.val.foo.bar chain
+ if not isinstance(node.parent, astroid.Attribute):
+ # FIXME:conf do some proper check for this...
+ node_str = node.as_string()
+ prefix = 'config.val.'
+ if node_str.startswith(prefix):
+ self._check_config(node, node_str[len(prefix):])
+
+ def _check_config(self, node, name):
+ """Check that we're accessing proper config options."""
+ if FAILED_LOAD:
+ if not ConfigChecker.printed_warning:
+ print("[WARN] Could not find configdata.yml. Please run "
+ "pylint from qutebrowser root.", file=sys.stderr)
+ print("Skipping some checks...", file=sys.stderr)
+ ConfigChecker.printed_warning = True
+ return
+ if name not in OPTIONS:
+ self.add_message('bad-config-option', node=node, args=name)
+
+
+def register(linter):
+ """Register this checker."""
+ linter.register_checker(ConfigChecker(linter))
+ global OPTIONS
+ global FAILED_LOAD
+ yaml_file = pathlib.Path('qutebrowser') / 'config' / 'configdata.yml'
+ if not yaml_file.exists():
+ OPTIONS = None
+ FAILED_LOAD = True
+ return
+ with yaml_file.open(mode='r', encoding='utf-8') as f:
+ OPTIONS = list(yaml.load(f))
diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/modeline.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/modeline.py
new file mode 100644
index 0000000..429974c
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/modeline.py
@@ -0,0 +1,63 @@
+# Copyright 2014-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# 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 <http://www.gnu.org/licenses/>.
+
+"""Checker for vim modelines in files."""
+
+import os.path
+import contextlib
+
+from pylint import interfaces, checkers
+
+
+class ModelineChecker(checkers.BaseChecker):
+
+ """Check for vim modelines in files."""
+
+ __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)}
+ options = ()
+ priority = -1
+
+ def process_module(self, node):
+ """Process the module."""
+ if os.path.basename(os.path.splitext(node.file)[0]) == '__init__':
+ return
+ max_lineno = 1
+ with contextlib.closing(node.stream()) as stream:
+ for (lineno, line) in enumerate(stream):
+ if lineno == 1 and line.startswith(b'#!'):
+ max_lineno += 1
+ continue
+ elif line.startswith(b'# vim:'):
+ if lineno > max_lineno:
+ self.add_message('modeline-position', line=lineno)
+ if (line.rstrip() != b'# vim: ft=python '
+ b'fileencoding=utf-8 sts=4 sw=4 et:'):
+ self.add_message('invalid-modeline', line=lineno)
+ break
+ else:
+ self.add_message('modeline-missing', line=1)
+
+
+def register(linter):
+ """Register the checker."""
+ linter.register_checker(ModelineChecker(linter))
diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/openencoding.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/openencoding.py
new file mode 100644
index 0000000..f577011
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/openencoding.py
@@ -0,0 +1,83 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://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/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/settrace.py b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/settrace.py
new file mode 100644
index 0000000..c82d646
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/pylint_checkers/qute_pylint/settrace.py
@@ -0,0 +1,49 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Custom astroid checker for set_trace calls."""
+
+from pylint.interfaces import IAstroidChecker
+from pylint.checkers import BaseChecker, utils
+
+
+class SetTraceChecker(BaseChecker):
+
+ """Custom astroid checker for set_trace calls."""
+
+ __implements__ = IAstroidChecker
+ name = 'settrace'
+ msgs = {
+ 'E9101': ('set_trace call found', 'set-trace', None),
+ }
+ priority = -1
+
+ @utils.check_messages('set-trace')
+ def visit_call(self, node):
+ """Visit a Call node."""
+ if hasattr(node, 'func'):
+ infer = utils.safe_infer(node.func)
+ if infer:
+ if getattr(node.func, 'name', None) == 'set_trace':
+ self.add_message('set-trace', node=node)
+
+
+def register(linter):
+ """Register this checker."""
+ linter.register_checker(SetTraceChecker(linter))
diff --git a/.config/qutebrowser/scripts/dev/pylint_checkers/setup.py b/.config/qutebrowser/scripts/dev/pylint_checkers/setup.py
new file mode 100644
index 0000000..7833c7d
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/pylint_checkers/setup.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python3
+
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2016-2018 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 <http://www.gnu.org/licenses/>.
+
+"""This is only here so we can install those plugins in tox.ini easily."""
+
+from setuptools import setup
+setup(name='qute_pylint', packages=['qute_pylint'])
diff --git a/.config/qutebrowser/scripts/dev/quit_segfault_test.sh b/.config/qutebrowser/scripts/dev/quit_segfault_test.sh
new file mode 100755
index 0000000..389f125
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/quit_segfault_test.sh
@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+[[ $PWD == */scripts ]] && cd ..
+
+echo > crash.log
+while :; do
+ exit=0
+ while (( exit == 0 )); do
+ duration=$(( RANDOM % 10000 ))
+ python3 -m qutebrowser --debug ":later $duration quit" http://www.heise.de/
+ exit=$?
+ done
+ echo "$(date) $exit $duration" >> crash.log
+done
diff --git a/.config/qutebrowser/scripts/dev/recompile_requirements.py b/.config/qutebrowser/scripts/dev/recompile_requirements.py
new file mode 100644
index 0000000..6e26145
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/recompile_requirements.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2016-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Script to regenerate requirements files in misc/requirements."""
+
+import re
+import sys
+import os.path
+import glob
+import subprocess
+import tempfile
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+from scripts import utils
+
+REPO_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+ '..', '..') # /scripts/dev -> /scripts -> /
+REQ_DIR = os.path.join(REPO_DIR, 'misc', 'requirements')
+
+
+def convert_line(line, comments):
+ """Convert the given requirement line to place into the output."""
+ for pattern, repl in comments['replace'].items():
+ line = re.sub(pattern, repl, line)
+
+ pkgname = line.split('=')[0]
+
+ if pkgname in comments['ignore']:
+ line = '# ' + line
+
+ try:
+ line += ' # ' + comments['comment'][pkgname]
+ except KeyError:
+ pass
+
+ try:
+ line += ' # rq.filter: {}'.format(comments['filter'][pkgname])
+ except KeyError:
+ pass
+
+ return line
+
+
+def read_comments(fobj):
+ """Find special comments in the config.
+
+ Args:
+ fobj: A file object for the config.
+
+ Return:
+ A dict with the parsed comment data.
+ """
+ comments = {
+ 'filter': {},
+ 'comment': {},
+ 'ignore': [],
+ 'replace': {},
+ }
+ for line in fobj:
+ if line.startswith('#@'):
+ command, args = line[2:].split(':', maxsplit=1)
+ command = command.strip()
+ args = args.strip()
+ if command == 'filter':
+ pkg, filt = args.split(' ', maxsplit=1)
+ comments['filter'][pkg] = filt
+ elif command == 'comment':
+ pkg, comment = args.split(' ', maxsplit=1)
+ comments['comment'][pkg] = comment
+ elif command == 'ignore':
+ comments['ignore'] += args.split(', ')
+ elif command == 'replace':
+ pattern, replacement = args.split(' ', maxsplit=1)
+ comments['replace'][pattern] = replacement
+ return comments
+
+
+def get_all_names():
+ """Get all requirement names based on filenames."""
+ for filename in glob.glob(os.path.join(REQ_DIR, 'requirements-*.txt-raw')):
+ basename = os.path.basename(filename)
+ yield basename[len('requirements-'):-len('.txt-raw')]
+
+
+def main():
+ """Re-compile the given (or all) requirement files."""
+ names = sys.argv[1:] if len(sys.argv) > 1 else sorted(get_all_names())
+
+ for name in names:
+ utils.print_title(name)
+ filename = os.path.join(REQ_DIR,
+ 'requirements-{}.txt-raw'.format(name))
+ if name == 'qutebrowser':
+ outfile = os.path.join(REPO_DIR, 'requirements.txt')
+ else:
+ outfile = os.path.join(REQ_DIR, 'requirements-{}.txt'.format(name))
+
+ with tempfile.TemporaryDirectory() as tmpdir:
+ pip_bin = os.path.join(tmpdir, 'bin', 'pip')
+ subprocess.run(['virtualenv', tmpdir], check=True)
+ subprocess.run([pip_bin, 'install', '-r', filename], check=True)
+ proc = subprocess.run([pip_bin, 'freeze'], check=True,
+ stdout=subprocess.PIPE)
+ reqs = proc.stdout.decode('utf-8')
+
+ with open(filename, 'r', encoding='utf-8') as f:
+ comments = read_comments(f)
+
+ with open(outfile, 'w', encoding='utf-8') as f:
+ f.write("# This file is automatically generated by "
+ "scripts/dev/recompile_requirements.py\n\n")
+ for line in reqs.splitlines():
+ f.write(convert_line(line, comments) + '\n')
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/run_profile.py b/.config/qutebrowser/scripts/dev/run_profile.py
new file mode 100755
index 0000000..93e0b61
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/run_profile.py
@@ -0,0 +1,93 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Profile qutebrowser."""
+
+import sys
+import cProfile
+import os.path
+import os
+import tempfile
+import subprocess
+import shutil
+import argparse
+import shlex
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+import qutebrowser.qutebrowser
+
+
+def parse_args():
+ """Parse commandline arguments.
+
+ Return:
+ A (namespace, remaining_args) tuple from argparse.
+ """
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--profile-tool', metavar='TOOL',
+ action='store', choices=['kcachegrind', 'snakeviz',
+ 'gprof2dot', 'none'],
+ default='snakeviz',
+ help="The tool to use to view the profiling data")
+ parser.add_argument('--profile-file', metavar='FILE', action='store',
+ help="The filename to use with --profile-tool=none")
+ return parser.parse_known_args()
+
+
+def main():
+ args, remaining = parse_args()
+ tempdir = tempfile.mkdtemp()
+
+ if args.profile_tool == 'none':
+ profilefile = os.path.join(os.getcwd(), args.profile_file)
+ else:
+ profilefile = os.path.join(tempdir, 'profile')
+
+ sys.argv = [sys.argv[0]] + remaining
+
+ profiler = cProfile.Profile()
+ profiler.runcall(qutebrowser.qutebrowser.main)
+
+ # If we have an exception after here, we don't want the qutebrowser
+ # exception hook to take over.
+ sys.excepthook = sys.__excepthook__
+ profiler.dump_stats(profilefile)
+
+ if args.profile_tool == 'none':
+ pass
+ elif args.profile_tool == 'gprof2dot':
+ # yep, shell=True. I know what I'm doing.
+ subprocess.run(
+ 'gprof2dot -f pstats {} | dot -Tpng | feh -F -'.format(
+ shlex.quote(profilefile)), shell=True)
+ elif args.profile_tool == 'kcachegrind':
+ callgraphfile = os.path.join(tempdir, 'callgraph')
+ subprocess.run(['pyprof2calltree', '-k', '-i', profilefile,
+ '-o', callgraphfile])
+ elif args.profile_tool == 'snakeviz':
+ subprocess.run(['snakeviz', profilefile])
+
+ shutil.rmtree(tempdir)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/run_pylint_on_tests.py b/.config/qutebrowser/scripts/dev/run_pylint_on_tests.py
new file mode 100644
index 0000000..7adf45f
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/run_pylint_on_tests.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Run pylint on tests.
+
+This is needed because pylint can't check a folder which isn't a package:
+https://bitbucket.org/logilab/pylint/issue/512/
+"""
+
+import os
+import os.path
+import sys
+import subprocess
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+from scripts import utils
+
+
+def main():
+ """Main entry point.
+
+ Return:
+ The pylint exit status.
+ """
+ utils.change_cwd()
+ files = []
+ for dirpath, _dirnames, filenames in os.walk('tests'):
+ for fn in filenames:
+ if os.path.splitext(fn)[1] == '.py':
+ files.append(os.path.join(dirpath, fn))
+
+ disabled = [
+ # pytest fixtures
+ 'redefined-outer-name',
+ 'unused-argument',
+ # things which are okay in tests
+ 'missing-docstring',
+ 'protected-access',
+ 'len-as-condition',
+ # directories without __init__.py...
+ 'import-error',
+ ]
+
+ toxinidir = sys.argv[1]
+ pythonpath = os.environ.get('PYTHONPATH', '').split(os.pathsep) + [
+ toxinidir,
+ ]
+
+ args = (['--disable={}'.format(','.join(disabled)),
+ '--ignored-modules=helpers,pytest,PyQt5'] +
+ sys.argv[2:] + files)
+ env = os.environ.copy()
+ env['PYTHONPATH'] = os.pathsep.join(pythonpath)
+
+ ret = subprocess.run(['pylint'] + args, env=env).returncode
+ return ret
+
+
+if __name__ == '__main__':
+ sys.exit(main())
diff --git a/.config/qutebrowser/scripts/dev/run_vulture.py b/.config/qutebrowser/scripts/dev/run_vulture.py
new file mode 100755
index 0000000..cb19d62
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/run_vulture.py
@@ -0,0 +1,196 @@
+#!/usr/bin/env python
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2015-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Run vulture on the source files and filter out false-positives."""
+
+import sys
+import os
+import re
+import tempfile
+import inspect
+import argparse
+
+import vulture
+
+import qutebrowser.app # pylint: disable=unused-import
+from qutebrowser.commands import cmdutils
+from qutebrowser.utils import utils
+from qutebrowser.browser.webkit import rfc6266
+# To run the decorators from there
+# pylint: disable=unused-import
+from qutebrowser.browser.webkit.network import webkitqutescheme
+# pylint: enable=unused-import
+from qutebrowser.browser import qutescheme
+from qutebrowser.config import configtypes
+
+
+def whitelist_generator(): # noqa
+ """Generator which yields lines to add to a vulture whitelist."""
+ # qutebrowser commands
+ for cmd in cmdutils.cmd_dict.values():
+ yield utils.qualname(cmd.handler)
+
+ # pyPEG2 classes
+ for name, member in inspect.getmembers(rfc6266, inspect.isclass):
+ for attr in ['grammar', 'regex']:
+ if hasattr(member, attr):
+ yield 'qutebrowser.browser.webkit.rfc6266.{}.{}'.format(name,
+ attr)
+
+ # PyQt properties
+ yield 'qutebrowser.mainwindow.statusbar.bar.StatusBar.color_flags'
+ yield 'qutebrowser.mainwindow.statusbar.url.UrlText.urltype'
+
+ # Not used yet, but soon (or when debugging)
+ yield 'qutebrowser.utils.debug.log_events'
+ yield 'qutebrowser.utils.debug.log_signals'
+ yield 'qutebrowser.utils.debug.qflags_key'
+ yield 'qutebrowser.utils.qtutils.QtOSError.qt_errno'
+ yield 'scripts.utils.bg_colors'
+
+ # Qt attributes
+ yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().baseUrl'
+ yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().content'
+ yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().encoding'
+ yield 'PyQt5.QtWebKit.QWebPage.ErrorPageExtensionReturn().fileNames'
+ yield 'PyQt5.QtWidgets.QStyleOptionViewItem.backgroundColor'
+
+ ## qute://... handlers
+ for name in qutescheme._HANDLERS: # pylint: disable=protected-access
+ name = name.replace('-', '_')
+ yield 'qutebrowser.browser.qutescheme.qute_' + name
+
+ # Other false-positives
+ yield 'qutebrowser.completion.models.listcategory.ListCategory().lessThan'
+ yield 'qutebrowser.utils.jinja.Loader.get_source'
+ yield 'qutebrowser.utils.log.QtWarningFilter.filter'
+ yield 'qutebrowser.browser.pdfjs.is_available'
+ yield 'qutebrowser.misc.guiprocess.spawn_output'
+ yield 'QEvent.posted'
+ yield 'log_stack' # from message.py
+ yield 'propagate' # logging.getLogger('...).propagate = False
+ # vulture doesn't notice the hasattr() and thus thinks netrc_used is unused
+ # in NetworkManager.on_authentication_required
+ yield 'PyQt5.QtNetwork.QNetworkReply.netrc_used'
+ yield 'qutebrowser.browser.downloads.last_used_directory'
+ yield 'PaintContext.clip' # from completiondelegate.py
+ yield 'logging.LogRecord.log_color' # from logging.py
+ yield 'scripts.utils.use_color' # from asciidoc2html.py
+ for attr in ['pyeval_output', 'log_clipboard', 'fake_clipboard']:
+ yield 'qutebrowser.misc.utilcmds.' + attr
+
+ for attr in ['fileno', 'truncate', 'closed', 'readable']:
+ yield 'qutebrowser.utils.qtutils.PyQIODevice.' + attr
+
+ for attr in ['msgs', 'priority', 'visit_attribute']:
+ yield 'scripts.dev.pylint_checkers.config.' + attr
+ for attr in ['visit_call', 'process_module']:
+ yield 'scripts.dev.pylint_checkers.modeline.' + attr
+
+ for name, _member in inspect.getmembers(configtypes, inspect.isclass):
+ yield 'qutebrowser.config.configtypes.' + name
+ yield 'qutebrowser.config.configexc.ConfigErrorDesc.traceback'
+ yield 'qutebrowser.config.configfiles.ConfigAPI.load_autoconfig'
+ yield 'types.ModuleType.c' # configfiles:read_config_py
+ for name in ['configdir', 'datadir']:
+ yield 'qutebrowser.config.configfiles.ConfigAPI.' + name
+
+ yield 'include_aliases'
+
+ for attr in ['_get_default_metavar_for_optional',
+ '_get_default_metavar_for_positional', '_metavar_formatter']:
+ yield 'scripts.dev.src2asciidoc.UsageFormatter.' + attr
+
+ # attrs
+ yield 'qutebrowser.browser.webkit.network.networkmanager.ProxyId.hostname'
+ yield 'qutebrowser.command.command.ArgInfo._validate_exclusive'
+ yield 'scripts.get_coredumpctl_traces.Line.uid'
+ yield 'scripts.get_coredumpctl_traces.Line.gid'
+ yield 'scripts.importer.import_moz_places.places.row_factory'
+
+
+def filter_func(item):
+ """Check if a missing function should be filtered or not.
+
+ Return:
+ True if the missing function should be filtered/ignored, False
+ otherwise.
+ """
+ return bool(re.fullmatch(r'[a-z]+[A-Z][a-zA-Z]+', item.name))
+
+
+def report(items):
+ """Generate a report based on the given vulture.Item's.
+
+ Based on vulture.Vulture.report, but we can't use that as we can't set the
+ properties which get used for the items.
+ """
+ output = []
+ for item in sorted(items,
+ key=lambda e: (e.filename.lower(), e.first_lineno)):
+ output.append(item.get_report())
+ return output
+
+
+def run(files):
+ """Run vulture over the given files."""
+ with tempfile.NamedTemporaryFile(mode='w', delete=False) as whitelist_file:
+ for line in whitelist_generator():
+ whitelist_file.write(line + '\n')
+
+ whitelist_file.close()
+
+ vult = vulture.Vulture(verbose=False)
+ vult.scavenge(files + [whitelist_file.name])
+
+ os.remove(whitelist_file.name)
+
+ filters = {
+ 'unused_funcs': filter_func,
+ 'unused_props': lambda item: False,
+ 'unused_vars': lambda item: False,
+ 'unused_attrs': lambda item: False,
+ }
+
+ items = []
+
+ for attr, func in filters.items():
+ sub_items = getattr(vult, attr)
+ for item in sub_items:
+ filtered = func(item)
+ if not filtered:
+ items.append(item)
+
+ return report(items)
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('files', nargs='*', default=['qutebrowser', 'scripts',
+ 'setup.py'])
+ args = parser.parse_args()
+ out = run(args.files)
+ for line in out:
+ print(line)
+ sys.exit(bool(out))
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/segfault_test.py b/.config/qutebrowser/scripts/dev/segfault_test.py
new file mode 100755
index 0000000..aaf495f
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/segfault_test.py
@@ -0,0 +1,120 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Tester for Qt segfaults with different harfbuzz engines."""
+
+import os
+import os.path
+import signal
+import sys
+import subprocess
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+from scripts import utils
+
+
+SCRIPT = """
+import sys
+
+from PyQt5.QtCore import QUrl
+from PyQt5.QtWidgets import QApplication
+from PyQt5.QtWebKitWidgets import QWebView
+
+def on_load_finished(ok):
+ if ok:
+ app.exit(0)
+ else:
+ app.exit(1)
+
+app = QApplication([])
+wv = QWebView()
+wv.loadFinished.connect(on_load_finished)
+wv.load(QUrl(sys.argv[1]))
+#wv.show()
+app.exec_()
+"""
+
+
+def print_ret(ret):
+ """Print information about an exit status."""
+ if ret == 0:
+ utils.print_col("success", 'green')
+ elif ret == -signal.SIGSEGV:
+ utils.print_col("segfault", 'red')
+ else:
+ utils.print_col("error {}".format(ret), 'yellow')
+ print()
+
+
+def main():
+ retvals = []
+ if len(sys.argv) < 2:
+ # pages which previously caused problems
+ pages = [
+ # ANGLE, https://bugreports.qt.io/browse/QTBUG-39723
+ ('http://www.binpress.com/', False),
+ ('http://david.li/flow/', False),
+ ('https://imzdl.com/', False),
+ # not reproducible
+ # https://bugreports.qt.io/browse/QTBUG-39847
+ ('http://www.20min.ch/', True),
+ # HarfBuzz, https://bugreports.qt.io/browse/QTBUG-39278
+ ('http://www.the-compiler.org/', True),
+ ('http://phoronix.com', True),
+ ('http://twitter.com', True),
+ # HarfBuzz #2, https://bugreports.qt.io/browse/QTBUG-36099
+ ('http://lenta.ru/', True),
+ # Unknown, https://bugreports.qt.io/browse/QTBUG-41360
+ ('http://salt.readthedocs.org/en/latest/topics/pillar/', True),
+ ]
+ else:
+ pages = [(e, True) for e in sys.argv[1:]]
+ for page, test_harfbuzz in pages:
+ utils.print_bold("==== {} ====".format(page))
+ if test_harfbuzz:
+ print("With system harfbuzz:")
+ ret = subprocess.run([sys.executable, '-c', SCRIPT, page]).returncode
+ print_ret(ret)
+ retvals.append(ret)
+ if test_harfbuzz:
+ print("With QT_HARFBUZZ=old:")
+ env = dict(os.environ)
+ env['QT_HARFBUZZ'] = 'old'
+ ret = subprocess.run([sys.executable, '-c', SCRIPT, page],
+ env=env).returncode
+ print_ret(ret)
+ retvals.append(ret)
+ print("With QT_HARFBUZZ=new:")
+ env = dict(os.environ)
+ env['QT_HARFBUZZ'] = 'new'
+ ret = subprocess.run([sys.executable, '-c', SCRIPT, page],
+ env=env).returncode
+ print_ret(ret)
+ retvals.append(ret)
+ if all(r == 0 for r in retvals):
+ sys.exit(0)
+ else:
+ sys.exit(1)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/src2asciidoc.py b/.config/qutebrowser/scripts/dev/src2asciidoc.py
new file mode 100755
index 0000000..cc00c37
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/src2asciidoc.py
@@ -0,0 +1,561 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Generate asciidoc source for qutebrowser based on docstrings."""
+
+import os
+import os.path
+import sys
+import shutil
+import inspect
+import subprocess
+import tempfile
+import argparse
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir,
+ os.pardir))
+
+# We import qutebrowser.app so all @cmdutils-register decorators are run.
+import qutebrowser.app
+from qutebrowser import qutebrowser, commands
+from qutebrowser.commands import cmdutils, argparser
+from qutebrowser.config import configdata, configtypes
+from qutebrowser.utils import docutils, usertypes
+from scripts import asciidoc2html, utils
+
+FILE_HEADER = """
+// DO NOT EDIT THIS FILE DIRECTLY!
+// It is autogenerated by running:
+// $ python3 scripts/dev/src2asciidoc.py
+// vim: readonly:
+
+""".lstrip()
+
+
+class UsageFormatter(argparse.HelpFormatter):
+
+ """Patched HelpFormatter to include some asciidoc markup in the usage.
+
+ This does some horrible things, but the alternative would be to reimplement
+ argparse.HelpFormatter while copying 99% of the code :-/
+ """
+
+ def _format_usage(self, usage, actions, groups, _prefix):
+ """Override _format_usage to not add the 'usage:' prefix."""
+ return super()._format_usage(usage, actions, groups, '')
+
+ def _get_default_metavar_for_optional(self, action):
+ """Do name transforming when getting metavar."""
+ return argparser.arg_name(action.dest.upper())
+
+ def _get_default_metavar_for_positional(self, action):
+ """Do name transforming when getting metavar."""
+ return argparser.arg_name(action.dest)
+
+ def _metavar_formatter(self, action, default_metavar):
+ """Override _metavar_formatter to add asciidoc markup to metavars.
+
+ Most code here is copied from Python 3.4's argparse.py.
+ """
+ if action.metavar is not None:
+ result = "'{}'".format(action.metavar)
+ elif action.choices is not None:
+ choice_strs = [str(choice) for choice in action.choices]
+ result = ('{' + ','.join('*{}*'.format(e) for e in choice_strs) +
+ '}')
+ else:
+ result = "'{}'".format(default_metavar)
+
+ def fmt(tuple_size):
+ """Format the result according to the tuple size."""
+ if isinstance(result, tuple):
+ return result
+ else:
+ return (result, ) * tuple_size
+ return fmt
+
+ def _format_actions_usage(self, actions, groups):
+ """Override _format_actions_usage to add asciidoc markup to flags.
+
+ Because argparse.py's _format_actions_usage is very complex, we first
+ monkey-patch the option strings to include the asciidoc markup, then
+ run the original method, then undo the patching.
+ """
+ old_option_strings = {}
+ for action in actions:
+ old_option_strings[action] = action.option_strings[:]
+ action.option_strings = ['*{}*'.format(s)
+ for s in action.option_strings]
+ ret = super()._format_actions_usage(actions, groups)
+ for action in actions:
+ action.option_strings = old_option_strings[action]
+ return ret
+
+
+def _open_file(name, mode='w'):
+ """Open a file with a preset newline/encoding mode."""
+ return open(name, mode, newline='\n', encoding='utf-8')
+
+
+def _get_cmd_syntax(_name, cmd):
+ """Get the command syntax for a command.
+
+ We monkey-patch the parser's formatter_class here to use our UsageFormatter
+ which adds some asciidoc markup.
+ """
+ old_fmt_class = cmd.parser.formatter_class
+ cmd.parser.formatter_class = UsageFormatter
+ usage = cmd.parser.format_usage().rstrip()
+ cmd.parser.formatter_class = old_fmt_class
+ return usage
+
+
+def _get_command_quickref(cmds):
+ """Generate the command quick reference."""
+ out = []
+ out.append('[options="header",width="75%",cols="25%,75%"]')
+ out.append('|==============')
+ out.append('|Command|Description')
+ for name, cmd in cmds:
+ desc = inspect.getdoc(cmd.handler).splitlines()[0]
+ out.append('|<<{},{}>>|{}'.format(name, name, desc))
+ out.append('|==============')
+ return '\n'.join(out)
+
+
+def _get_setting_quickref():
+ """Generate the settings quick reference."""
+ out = []
+ out.append('')
+ out.append('[options="header",width="75%",cols="25%,75%"]')
+ out.append('|==============')
+ out.append('|Setting|Description')
+ for opt in sorted(configdata.DATA.values()):
+ desc = opt.description.splitlines()[0]
+ out.append('|<<{},{}>>|{}'.format(opt.name, opt.name, desc))
+ out.append('|==============')
+ return '\n'.join(out)
+
+
+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] and
+ # pylint: enable=protected-access
+ issubclass(e, configtypes.BaseType))
+ yield from inspect.getmembers(configtypes, predicate)
+
+
+def _get_setting_types_quickref():
+ """Generate the setting types quick reference."""
+ out = []
+ out.append('[[types]]')
+ out.append('[options="header",width="75%",cols="25%,75%"]')
+ out.append('|==============')
+ out.append('|Type|Description')
+
+ for name, typ in _get_configtypes():
+ parser = docutils.DocstringParser(typ)
+ desc = parser.short_desc
+ if parser.long_desc:
+ desc += '\n\n' + parser.long_desc
+ out.append('|{}|{}'.format(name, desc))
+
+ out.append('|==============')
+ return '\n'.join(out)
+
+
+def _get_command_doc(name, cmd):
+ """Generate the documentation for a command."""
+ output = ['[[{}]]'.format(name)]
+ output += ['=== {}'.format(name)]
+ syntax = _get_cmd_syntax(name, cmd)
+ if syntax != name:
+ output.append('Syntax: +:{}+'.format(syntax))
+ output.append("")
+ parser = docutils.DocstringParser(cmd.handler)
+ output.append(parser.short_desc)
+ if parser.long_desc:
+ output.append("")
+ output.append(parser.long_desc)
+
+ output += list(_get_command_doc_args(cmd, parser))
+ output += list(_get_command_doc_count(cmd, parser))
+ output += list(_get_command_doc_notes(cmd))
+
+ output.append("")
+ output.append("")
+ return '\n'.join(output)
+
+
+def _get_command_doc_args(cmd, parser):
+ """Get docs for the arguments of a command.
+
+ Args:
+ cmd: The Command to get the docs for.
+ parser: The DocstringParser to use.
+
+ Yield:
+ Strings which should be added to the docs.
+ """
+ if cmd.pos_args:
+ yield ""
+ yield "==== positional arguments"
+ for arg, name in cmd.pos_args:
+ try:
+ yield "* +'{}'+: {}".format(name, parser.arg_descs[arg])
+ except KeyError as e:
+ raise KeyError("No description for arg {} of command "
+ "'{}'!".format(e, cmd.name)) from e
+
+ if cmd.opt_args:
+ yield ""
+ yield "==== optional arguments"
+ for arg, (long_flag, short_flag) in cmd.opt_args.items():
+ try:
+ yield '* +*{}*+, +*{}*+: {}'.format(short_flag, long_flag,
+ parser.arg_descs[arg])
+ except KeyError as e:
+ raise KeyError("No description for arg {} of command "
+ "'{}'!".format(e, cmd.name)) from e
+
+
+def _get_command_doc_count(cmd, parser):
+ """Get docs for the count of a command.
+
+ Args:
+ cmd: The Command to get the docs for.
+ parser: The DocstringParser to use.
+
+ Yield:
+ Strings which should be added to the docs.
+ """
+ for param in inspect.signature(cmd.handler).parameters.values():
+ if cmd.get_arg_info(param).count:
+ yield ""
+ yield "==== count"
+ try:
+ yield parser.arg_descs[param.name]
+ except KeyError as e:
+ raise KeyError("No description for count arg {!r} of command "
+ "{!r}!".format(param.name, cmd.name)) from e
+
+
+def _get_command_doc_notes(cmd):
+ """Get docs for the notes of a command.
+
+ Args:
+ cmd: The Command to get the docs for.
+ parser: The DocstringParser to use.
+
+ Yield:
+ Strings which should be added to the docs.
+ """
+ if (cmd.maxsplit is not None or cmd.no_cmd_split or
+ cmd.no_replace_variables and cmd.name != "spawn"):
+ yield ""
+ yield "==== note"
+ if cmd.maxsplit is not None:
+ yield ("* This command does not split arguments after the last "
+ "argument and handles quotes literally.")
+ if cmd.no_cmd_split:
+ yield ("* With this command, +;;+ is interpreted literally "
+ "instead of splitting off a second command.")
+ if cmd.no_replace_variables and cmd.name != "spawn":
+ yield r"* This command does not replace variables like +\{url\}+."
+
+
+def _get_action_metavar(action, nargs=1):
+ """Get the metavar to display for an argparse action.
+
+ Args:
+ action: The argparse action to get the metavar for.
+ nargs: The nargs setting for the related argument.
+ """
+ if action.metavar is not None:
+ if isinstance(action.metavar, str):
+ elems = [action.metavar] * nargs
+ else:
+ elems = action.metavar
+ return ' '.join("'{}'".format(e) for e in elems)
+ elif action.choices is not None:
+ choices = ','.join(str(e) for e in action.choices)
+ return "'{{{}}}'".format(choices)
+ else:
+ return "'{}'".format(action.dest.upper())
+
+
+def _format_action_args(action):
+ """Get an argument string based on an argparse action."""
+ if action.nargs is None:
+ return _get_action_metavar(action)
+ elif action.nargs == '?':
+ return '[{}]'.format(_get_action_metavar(action))
+ elif action.nargs == '*':
+ return '[{mv} [{mv} ...]]'.format(mv=_get_action_metavar(action))
+ elif action.nargs == '+':
+ return '{mv} [{mv} ...]'.format(mv=_get_action_metavar(action))
+ elif action.nargs == '...':
+ return '...'
+ else:
+ return _get_action_metavar(action, nargs=action.nargs)
+
+
+def _format_action(action):
+ """Get an invocation string/help from an argparse action."""
+ if action.help == argparse.SUPPRESS:
+ return None
+ if not action.option_strings:
+ invocation = '*{}*::'.format(_get_action_metavar(action))
+ else:
+ parts = []
+ if action.nargs == 0:
+ # Doesn't take a value, so the syntax is -s, --long
+ parts += ['*{}*'.format(s) for s in action.option_strings]
+ else:
+ # Takes a value, so the syntax is -s ARGS or --long ARGS.
+ args_string = _format_action_args(action)
+ for opt in action.option_strings:
+ parts.append('*{}* {}'.format(opt, args_string))
+ invocation = ', '.join(parts) + '::'
+ return '{}\n {}\n'.format(invocation, action.help)
+
+
+def generate_commands(filename):
+ """Generate the complete commands section."""
+ with _open_file(filename) as f:
+ f.write(FILE_HEADER)
+ f.write("= Commands\n\n")
+ f.write(commands.__doc__)
+ normal_cmds = []
+ other_cmds = []
+ debug_cmds = []
+ for name, cmd in cmdutils.cmd_dict.items():
+ if cmd.deprecated:
+ continue
+ if usertypes.KeyMode.normal not in cmd.modes:
+ other_cmds.append((name, cmd))
+ elif cmd.debug:
+ debug_cmds.append((name, cmd))
+ else:
+ normal_cmds.append((name, cmd))
+ normal_cmds.sort()
+ other_cmds.sort()
+ debug_cmds.sort()
+ f.write("\n")
+ f.write("== Normal commands\n")
+ f.write(".Quick reference\n")
+ f.write(_get_command_quickref(normal_cmds) + '\n')
+ for name, cmd in normal_cmds:
+ f.write(_get_command_doc(name, cmd))
+ f.write("\n")
+ f.write("== Commands not usable in normal mode\n")
+ f.write(".Quick reference\n")
+ f.write(_get_command_quickref(other_cmds) + '\n')
+ for name, cmd in other_cmds:
+ f.write(_get_command_doc(name, cmd))
+ f.write("\n")
+ f.write("== Debugging commands\n")
+ f.write("These commands are mainly intended for debugging. They are "
+ "hidden if qutebrowser was started without the "
+ "`--debug`-flag.\n")
+ f.write("\n")
+ f.write(".Quick reference\n")
+ f.write(_get_command_quickref(debug_cmds) + '\n')
+ for name, cmd in debug_cmds:
+ f.write(_get_command_doc(name, cmd))
+
+
+def _generate_setting_backend_info(f, opt):
+ """Generate backend information for the given option."""
+ all_backends = [usertypes.Backend.QtWebKit, usertypes.Backend.QtWebEngine]
+ if opt.raw_backends is not None:
+ for name, conditional in sorted(opt.raw_backends.items()):
+ if conditional is True:
+ pass
+ elif conditional is False:
+ f.write("\nOn {}, this setting is unavailable.\n".format(name))
+ else:
+ f.write("\nOn {}, this setting requires {} or newer.\n"
+ .format(name, conditional))
+ elif opt.backends == all_backends:
+ pass
+ elif opt.backends == [usertypes.Backend.QtWebKit]:
+ f.write("\nThis setting is only available with the QtWebKit "
+ "backend.\n")
+ elif opt.backends == [usertypes.Backend.QtWebEngine]:
+ f.write("\nThis setting is only available with the QtWebEngine "
+ "backend.\n")
+ else:
+ raise ValueError("Invalid value {!r} for opt.backends"
+ .format(opt.backends))
+
+
+def _generate_setting_option(f, opt):
+ """Generate documentation for a single section."""
+ f.write("\n")
+ f.write('[[{}]]'.format(opt.name) + "\n")
+ f.write("=== {}".format(opt.name) + "\n")
+ f.write(opt.description + "\n")
+ if opt.restart:
+ f.write("This setting requires a restart.\n")
+ if opt.supports_pattern:
+ f.write("\nThis setting supports URL patterns.\n")
+ if opt.no_autoconfig:
+ f.write("\nThis setting can only be set in config.py.\n")
+ f.write("\n")
+ typ = opt.typ.get_name().replace(',', '&#44;')
+ f.write('Type: <<types,{typ}>>\n'.format(typ=typ))
+ f.write("\n")
+
+ valid_values = opt.typ.get_valid_values()
+ if valid_values is not None and valid_values.generate_docs:
+ f.write("Valid values:\n")
+ f.write("\n")
+ for val in valid_values:
+ try:
+ desc = valid_values.descriptions[val]
+ f.write(" * +{}+: {}".format(val, desc) + "\n")
+ except KeyError:
+ f.write(" * +{}+".format(val) + "\n")
+ f.write("\n")
+
+ f.write("Default: {}\n".format(opt.typ.to_doc(opt.default)))
+ _generate_setting_backend_info(f, opt)
+
+
+def generate_settings(filename):
+ """Generate the complete settings section."""
+ configdata.init()
+ with _open_file(filename) as f:
+ f.write(FILE_HEADER)
+ f.write("= Setting reference\n\n")
+ f.write("== All settings\n")
+ f.write(_get_setting_quickref() + "\n")
+ for opt in sorted(configdata.DATA.values()):
+ _generate_setting_option(f, opt)
+ f.write("\n== Setting types\n")
+ f.write(_get_setting_types_quickref() + "\n")
+
+
+def _format_block(filename, what, data):
+ """Format a block in a file.
+
+ The block is delimited by markers like these:
+ // QUTE_*_START
+ ...
+ // QUTE_*_END
+
+ The * part is the part which should be given as 'what'.
+
+ Args:
+ filename: The file to change.
+ what: What to change (authors, options, etc.)
+ data; A list of strings which is the new data.
+ """
+ what = what.upper()
+ oshandle, tmpname = tempfile.mkstemp()
+ try:
+ with _open_file(filename, mode='r') as infile, \
+ _open_file(oshandle, mode='w') as temp:
+ found_start = False
+ found_end = False
+ for line in infile:
+ if line.strip() == '// QUTE_{}_START'.format(what):
+ temp.write(line)
+ temp.write(''.join(data))
+ found_start = True
+ elif line.strip() == '// QUTE_{}_END'.format(what.upper()):
+ temp.write(line)
+ found_end = True
+ elif (not found_start) or found_end:
+ temp.write(line)
+ if not found_start:
+ raise Exception("Marker '// QUTE_{}_START' not found in "
+ "'{}'!".format(what, filename))
+ elif not found_end:
+ raise Exception("Marker '// QUTE_{}_END' not found in "
+ "'{}'!".format(what, filename))
+ except:
+ os.remove(tmpname)
+ raise
+ else:
+ os.remove(filename)
+ shutil.move(tmpname, filename)
+
+
+def regenerate_manpage(filename):
+ """Update manpage OPTIONS using an argparse parser."""
+ parser = qutebrowser.get_argparser()
+ groups = []
+ # positionals, optionals and user-defined groups
+ # pylint: disable=protected-access
+ for group in parser._action_groups:
+ groupdata = []
+ groupdata.append('=== {}'.format(group.title))
+ if group.description is not None:
+ groupdata.append(group.description)
+ for action in group._group_actions:
+ action_data = _format_action(action)
+ if action_data is not None:
+ groupdata.append(action_data)
+ groups.append('\n'.join(groupdata))
+ # pylint: enable=protected-access
+ options = '\n'.join(groups)
+ # epilog
+ if parser.epilog is not None:
+ options += parser.epilog
+ _format_block(filename, 'options', options)
+
+
+def regenerate_cheatsheet():
+ """Generate cheatsheet PNGs based on the SVG."""
+ files = [
+ ('doc/img/cheatsheet-small.png', 300, 185),
+ ('doc/img/cheatsheet-big.png', 3342, 2060),
+ ]
+
+ for filename, x, y in files:
+ subprocess.run(['inkscape', '-e', filename, '-b', 'white',
+ '-w', str(x), '-h', str(y),
+ 'misc/cheatsheet.svg'], check=True)
+
+
+def main():
+ """Regenerate all documentation."""
+ utils.change_cwd()
+ print("Generating manpage...")
+ regenerate_manpage('doc/qutebrowser.1.asciidoc')
+ print("Generating settings help...")
+ generate_settings('doc/help/settings.asciidoc')
+ print("Generating command help...")
+ generate_commands('doc/help/commands.asciidoc')
+ if '--cheatsheet' in sys.argv:
+ print("Regenerating cheatsheet .pngs")
+ regenerate_cheatsheet()
+ if '--html' in sys.argv:
+ asciidoc2html.main()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/standardpaths_tester.py b/.config/qutebrowser/scripts/dev/standardpaths_tester.py
new file mode 100644
index 0000000..27b8382
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/standardpaths_tester.py
@@ -0,0 +1,69 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2017-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Show various QStandardPath paths."""
+
+import os
+import sys
+
+from PyQt5.QtCore import (QT_VERSION_STR, PYQT_VERSION_STR, qVersion,
+ QStandardPaths, QCoreApplication)
+
+
+def print_header():
+ """Show system information."""
+ print("Python {}".format(sys.version))
+ print("os.name: {}".format(os.name))
+ print("sys.platform: {}".format(sys.platform))
+ print()
+
+ print("Qt {}, compiled {}".format(qVersion(), QT_VERSION_STR))
+ print("PyQt {}".format(PYQT_VERSION_STR))
+ print()
+
+
+def print_paths():
+ """Print all QStandardPaths.StandardLocation members."""
+ for name, obj in vars(QStandardPaths).items():
+ if isinstance(obj, QStandardPaths.StandardLocation):
+ location = QStandardPaths.writableLocation(obj)
+ print("{:25} {}".format(name, location))
+
+
+def main():
+ print_header()
+
+ print("No QApplication")
+ print("===============")
+ print()
+ print_paths()
+
+ app = QCoreApplication(sys.argv)
+ app.setApplicationName("qapp_name")
+
+ print()
+ print("With QApplication")
+ print("=================")
+ print()
+ print_paths()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/strip_whitespace.sh b/.config/qutebrowser/scripts/dev/strip_whitespace.sh
new file mode 100644
index 0000000..ee14278
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/strip_whitespace.sh
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+# Strip trailing whitespace from files in this repo
+
+find qutebrowser scripts tests \
+ -type f \( \
+ -name '*.py' -o \
+ -name '*.feature' -o \
+ -name '*.sh' \
+ \) -exec sed -i 's/ \+$//' {} +
diff --git a/.config/qutebrowser/scripts/dev/ua_fetch.py b/.config/qutebrowser/scripts/dev/ua_fetch.py
new file mode 100755
index 0000000..75ce4c2
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/ua_fetch.py
@@ -0,0 +1,122 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2015-2018 lamarpavel
+# Copyright 2015-2018 Alexey Nabrodov (Averrin)
+# Copyright 2015-2018 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 <http://www.gnu.org/licenses/>.
+
+
+"""Fetch list of popular user-agents.
+
+The script is based on a gist posted by github.com/averrin, the output of this
+script is formatted to be pasted into configdata.yml
+"""
+
+import requests
+from lxml import html # pylint: disable=import-error
+
+
+def fetch():
+ """Fetch list of popular user-agents.
+
+ Return:
+ List of relevant strings.
+ """
+ url = 'https://techblog.willshouse.com/2012/01/03/most-common-user-agents/'
+ page = requests.get(url)
+ page = html.fromstring(page.text)
+ path = '//*[@id="post-2229"]/div[2]/table/tbody'
+ return page.xpath(path)[0]
+
+
+def filter_list(complete_list, browsers):
+ """Filter the received list based on a look up table.
+
+ The LUT should be a dictionary of the format {browser: versions}, where
+ 'browser' is the name of the browser (eg. "Firefox") as string and
+ 'versions' is a set of different versions of this browser that should be
+ included when found (eg. {"Linux", "MacOSX"}). This function returns a
+ dictionary with the same keys as the LUT, but storing lists of tuples
+ (user_agent, browser_description) as values.
+ """
+ # pylint: disable=too-many-nested-blocks
+ table = {}
+ for entry in complete_list:
+ # Tuple of (user_agent, browser_description)
+ candidate = (entry[1].text_content(), entry[2].text_content())
+ for name in browsers:
+ found = False
+ if name.lower() in candidate[1].lower():
+ for version in browsers[name]:
+ if version.lower() in candidate[1].lower():
+ if table.get(name) is None:
+ table[name] = []
+ table[name].append(candidate)
+ browsers[name].remove(version)
+ found = True
+ break
+ if found:
+ break
+ return table
+
+
+def add_diversity(table):
+ """Insert a few additional entries for diversity into the dict.
+
+ (as returned by filter_list())
+ """
+ table["Obscure"] = [
+ ('Mozilla/5.0 (compatible; Googlebot/2.1; '
+ '+http://www.google.com/bot.html',
+ "Google Bot"),
+ ('Wget/1.16.1 (linux-gnu)',
+ "wget 1.16.1"),
+ ('curl/7.40.0',
+ "curl 7.40.0"),
+ ('Mozilla/5.0 (Linux; U; Android 7.1.2) AppleWebKit/534.30 '
+ '(KHTML, like Gecko) Version/4.0 Mobile Safari/534.30',
+ "Mobile Generic Android"),
+ ('Mozilla/5.0 (Windows NT 6.1; WOW64; Trident/7.0; rv:11.0) like '
+ 'Gecko',
+ "IE 11.0 for Desktop Win7 64-bit"),
+ ]
+ return table
+
+
+def main():
+ """Generate user agent code."""
+ fetched = fetch()
+ lut = {
+ "Firefox": {"Win", "MacOSX", "Linux", "Android"},
+ "Chrome": {"Win", "MacOSX", "Linux"},
+ "Safari": {"MacOSX", "iOS"}
+ }
+ filtered = filter_list(fetched, lut)
+ filtered = add_diversity(filtered)
+
+ tab = " "
+ for browser in ["Firefox", "Safari", "Chrome", "Obscure"]:
+ for it in filtered[browser]:
+ print('{}- - "{}"'.format(3 * tab, it[0]))
+ desc = it[1].replace('\xa0', ' ').replace(' ', ' ')
+ print("{}- {}".format(4 * tab, desc))
+ print("")
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dev/update_3rdparty.py b/.config/qutebrowser/scripts/dev/update_3rdparty.py
new file mode 100755
index 0000000..c40015d
--- /dev/null
+++ b/.config/qutebrowser/scripts/dev/update_3rdparty.py
@@ -0,0 +1,166 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2015 Daniel Schadt
+# Copyright 2016-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Update all third-party-modules."""
+
+import argparse
+import urllib.request
+import urllib.error
+import shutil
+import json
+import os
+import sys
+
+sys.path.insert(
+ 0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
+from scripts import dictcli
+from qutebrowser.config import configdata
+
+
+def get_latest_pdfjs_url():
+ """Get the URL of the latest pdf.js prebuilt package.
+
+ Returns a (version, url)-tuple.
+ """
+ github_api = 'https://api.github.com'
+ endpoint = 'repos/mozilla/pdf.js/releases/latest'
+ request_url = '{}/{}'.format(github_api, endpoint)
+ with urllib.request.urlopen(request_url) as fp:
+ data = json.loads(fp.read().decode('utf-8'))
+
+ download_url = data['assets'][0]['browser_download_url']
+ version_name = data['name']
+ return (version_name, download_url)
+
+
+def update_pdfjs(target_version=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')
+ """
+ if target_version is None:
+ version, url = get_latest_pdfjs_url()
+ else:
+ # We need target_version as x.y.z, without the 'v' prefix, though the
+ # user might give it on the command line
+ if target_version.startswith('v'):
+ target_version = target_version[1:]
+ # version should have the prefix to be consistent with the return value
+ # of get_latest_pdfjs_url()
+ version = 'v' + target_version
+ url = ('https://github.com/mozilla/pdf.js/releases/download/'
+ 'v{0}/pdfjs-{0}-dist.zip').format(target_version)
+
+ 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))
+ try:
+ (archive_path, _headers) = urllib.request.urlretrieve(url)
+ except urllib.error.HTTPError as error:
+ print("Could not retrieve pdfjs {}: {}".format(version, error))
+ return
+ if os.path.isdir(target_path):
+ print("Removing old version in {}".format(target_path))
+ shutil.rmtree(target_path)
+ os.makedirs(target_path)
+ print("Extracting new version")
+ with open(archive_path, 'rb') as archive:
+ shutil.unpack_archive(archive, target_path, 'zip')
+ urllib.request.urlcleanup()
+
+
+def update_dmg_makefile():
+ """Update fancy-dmg Makefile.
+
+ See https://el-tramo.be/blog/fancy-dmg/
+ """
+ print("Updating fancy-dmg Makefile...")
+ url = 'https://raw.githubusercontent.com/remko/fancy-dmg/master/Makefile'
+ target_path = os.path.join('scripts', 'dev', 'Makefile-dmg')
+ urllib.request.urlretrieve(url, target_path)
+ urllib.request.urlcleanup()
+
+
+def update_ace():
+ """Update ACE.
+
+ See https://ace.c9.io/ and https://github.com/ajaxorg/ace-builds/
+ """
+ print("Updating ACE...")
+ url = 'https://raw.githubusercontent.com/ajaxorg/ace-builds/master/src/ace.js'
+ target_path = os.path.join('tests', 'end2end', 'data', 'hints', 'ace',
+ 'ace.js')
+ urllib.request.urlretrieve(url, target_path)
+ urllib.request.urlcleanup()
+
+
+def test_dicts():
+ """Test available dictionaries."""
+ configdata.init()
+ for lang in dictcli.available_languages():
+ print('Testing dictionary {}... '.format(lang.code), end='')
+ lang_url = urllib.parse.urljoin(dictcli.API_URL, lang.remote_path)
+ 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))
+
+
+def run(ace=False, pdfjs=True, fancy_dmg=False, pdfjs_version=None,
+ dicts=False):
+ """Update components based on the given arguments."""
+ if pdfjs:
+ update_pdfjs(pdfjs_version)
+ if ace:
+ update_ace()
+ if fancy_dmg:
+ update_dmg_makefile()
+ if dicts:
+ test_dicts()
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument(
+ '--pdfjs', '-p',
+ help='Specify pdfjs version. If not given, '
+ 'the latest version is used.',
+ required=False, metavar='VERSION')
+ parser.add_argument('--fancy-dmg', help="Update fancy-dmg Makefile",
+ action='store_true')
+ parser.add_argument(
+ '--dicts', '-d',
+ help='Test whether all available dictionaries '
+ 'can be reached at the remote repository.',
+ required=False, action='store_true')
+ args = parser.parse_args()
+ run(ace=True, pdfjs=True, fancy_dmg=args.fancy_dmg,
+ pdfjs_version=args.pdfjs, dicts=args.dicts)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/dictcli.py b/.config/qutebrowser/scripts/dictcli.py
new file mode 100755
index 0000000..4017159
--- /dev/null
+++ b/.config/qutebrowser/scripts/dictcli.py
@@ -0,0 +1,283 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2017-2018 Michal Siedlaczek <michal.siedlaczek@gmail.com>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+"""A script installing Hunspell dictionaries.
+
+Use: python -m scripts.dictcli [-h] {list,update,remove-old,install} ...
+"""
+
+import argparse
+import base64
+import json
+import os
+import sys
+import re
+import urllib.request
+
+import attr
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
+from qutebrowser.browser.webengine import spell
+from qutebrowser.config import configdata
+from qutebrowser.utils import standarddir
+
+
+API_URL = 'https://chromium.googlesource.com/chromium/deps/hunspell_dictionaries.git/+/master/'
+
+
+class InvalidLanguageError(Exception):
+
+ """Raised when requesting invalid languages."""
+
+ def __init__(self, invalid_langs):
+ msg = 'invalid languages: {}'.format(', '.join(invalid_langs))
+ super().__init__(msg)
+
+
+@attr.s
+class Language:
+
+ """Dictionary language specs."""
+
+ code = attr.ib()
+ name = attr.ib()
+ remote_filename = attr.ib()
+ local_filename = attr.ib(default=None)
+ _file_extension = attr.ib('bdic', init=False)
+
+ def __attrs_post_init__(self):
+ if self.local_filename is None:
+ self.local_filename = spell.local_filename(self.code)
+
+ @property
+ def remote_path(self):
+ """Resolve the filename with extension the remote dictionary."""
+ return '.'.join([self.remote_filename, self._file_extension])
+
+ @property
+ def local_path(self):
+ """Resolve the filename with extension the local dictionary."""
+ if self.local_filename is None:
+ return None
+ return '.'.join([self.local_filename, self._file_extension])
+
+ @property
+ def remote_version(self):
+ """Resolve the version of the local dictionary."""
+ return spell.version(self.remote_path)
+
+ @property
+ def local_version(self):
+ """Resolve the version of the local dictionary."""
+ local_path = self.local_path
+ if local_path is None:
+ return None
+ return spell.version(local_path)
+
+
+def get_argparser():
+ """Get the argparse parser."""
+ desc = 'Install and manage Hunspell dictionaries for QtWebEngine.'
+ parser = argparse.ArgumentParser(prog='dictcli',
+ description=desc)
+ subparsers = parser.add_subparsers(help='Command', dest='cmd')
+ subparsers.required = True
+ subparsers.add_parser('list',
+ help='Display the list of available languages.')
+ subparsers.add_parser('update',
+ help='Update dictionaries')
+ subparsers.add_parser('remove-old',
+ help='Remove old versions of dictionaries.')
+
+ install_parser = subparsers.add_parser('install',
+ help='Install dictionaries')
+ install_parser.add_argument('language',
+ nargs='*',
+ help="A list of languages to install.")
+
+ return parser
+
+
+def version_str(version):
+ return '.'.join(str(n) for n in version)
+
+
+def print_list(languages):
+ """Print the list of available languages."""
+ pat = '{:<7}{:<26}{:<8}{:<5}'
+ print(pat.format('Code', 'Name', 'Version', 'Installed'))
+ for lang in languages:
+ remote_version = version_str(lang.remote_version)
+ local_version = '-'
+ if lang.local_version is not None:
+ local_version = version_str(lang.local_version)
+ if lang.local_version < lang.remote_version:
+ local_version += ' - update available!'
+ print(pat.format(lang.code, lang.name, remote_version, local_version))
+
+
+def valid_languages():
+ """Return a mapping from valid language codes to their names."""
+ option = configdata.DATA['spellcheck.languages']
+ return option.typ.valtype.valid_values.descriptions
+
+
+def parse_entry(entry):
+ """Parse an entry from the remote API."""
+ dict_re = re.compile(r"""
+ (?P<filename>(?P<code>[a-z]{2}(-[A-Z]{2})?).*)\.bdic
+ """, re.VERBOSE)
+ match = dict_re.fullmatch(entry['name'])
+ if match is not None:
+ return match.group('code'), match.group('filename')
+ else:
+ return None
+
+
+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:]
+ 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]
+
+
+def latest_yet(code2file, code, filename):
+ """Determine whether the latest version so far."""
+ if code not in code2file:
+ return True
+ return spell.version(code2file[code]) < spell.version(filename)
+
+
+def available_languages():
+ """Return a list of Language objects of all available languages."""
+ lang_map = valid_languages()
+ api_list = language_list_from_api()
+ code2file = {}
+ for code, filename in api_list:
+ if latest_yet(code2file, code, filename):
+ code2file[code] = filename
+ return [
+ Language(code, name, code2file[code])
+ for code, name in lang_map.items()
+ if code in code2file
+ ]
+
+
+def download_dictionary(url, dest):
+ """Download a decoded dictionary file."""
+ response = urllib.request.urlopen(url)
+ decoded = base64.decodebytes(response.read())
+ with open(dest, 'bw') as dict_file:
+ dict_file.write(decoded)
+
+
+def filter_languages(languages, selected):
+ """Filter a list of languages based on an inclusion list.
+
+ Args:
+ languages: a list of languages to filter
+ selected: a list of keys to select
+ """
+ filtered_languages = []
+ for language in languages:
+ if language.code in selected:
+ filtered_languages.append(language)
+ selected.remove(language.code)
+ if selected:
+ raise InvalidLanguageError(selected)
+ return filtered_languages
+
+
+def install_lang(lang):
+ """Install a single lang given by the argument."""
+ lang_url = API_URL + lang.remote_path + '?format=TEXT'
+ if not os.path.isdir(spell.dictionary_dir()):
+ msg = '{} does not exist, creating the directory'
+ print(msg.format(spell.dictionary_dir()))
+ os.makedirs(spell.dictionary_dir())
+ print('Downloading {}'.format(lang_url))
+ dest = os.path.join(spell.dictionary_dir(), lang.remote_path)
+ download_dictionary(lang_url, dest)
+ print('Done.')
+
+
+def install(languages):
+ """Install languages."""
+ for lang in languages:
+ try:
+ print('Installing {}: {}'.format(lang.code, lang.name))
+ install_lang(lang)
+ except PermissionError as e:
+ sys.exit(str(e))
+
+
+def update(languages):
+ """Update the given languages."""
+ installed = [lang for lang in languages if lang.local_version is not None]
+ for lang in installed:
+ if lang.local_version < lang.remote_version:
+ print('Upgrading {} from {} to {}'.format(
+ lang.code,
+ version_str(lang.local_version),
+ version_str(lang.remote_version)))
+ install_lang(lang)
+
+
+def remove_old(languages):
+ """Remove old versions of languages."""
+ installed = [lang for lang in languages if lang.local_version is not None]
+ for lang in installed:
+ local_files = spell.local_files(lang.code)
+ for old_file in local_files[1:]:
+ os.remove(os.path.join(spell.dictionary_dir(), old_file))
+
+
+def main():
+ if configdata.DATA is None:
+ configdata.init()
+ standarddir.init(None)
+
+ parser = get_argparser()
+ argv = sys.argv[1:]
+ args = parser.parse_args(argv)
+ languages = available_languages()
+ if args.cmd == 'list':
+ print_list(languages)
+ elif args.cmd == 'update':
+ update(languages)
+ elif args.cmd == 'remove-old':
+ remove_old(languages)
+ elif not args.language:
+ sys.exit('You must provide a list of languages to install.')
+ else:
+ try:
+ install(filter_languages(languages, args.language))
+ except InvalidLanguageError as e:
+ print(e)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/hist_importer.py b/.config/qutebrowser/scripts/hist_importer.py
new file mode 100755
index 0000000..914701a
--- /dev/null
+++ b/.config/qutebrowser/scripts/hist_importer.py
@@ -0,0 +1,174 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2017-2018 Florian Bruhin (The Compiler) <mail@qutebrowser.org>
+# Copyright 2017-2018 Josefson Souza <josefson.br@gmail.com>
+
+# 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 <http://www.gnu.org/licenses/>.
+
+
+"""Tool to import browser history from other browsers."""
+
+
+import argparse
+import sqlite3
+import sys
+import os
+
+
+class Error(Exception):
+
+ """Exception for errors in this module."""
+
+ pass
+
+
+def parse():
+ """Parse command line arguments."""
+ description = ("This program is meant to extract browser history from your"
+ " previous browser and import them into qutebrowser.")
+ epilog = ("Databases:\n\n\tqutebrowser: Is named 'history.sqlite' and can "
+ "be found at your --basedir. In order to find where your "
+ "basedir is you can run ':open qute:version' inside qutebrowser."
+ "\n\n\tFirefox: Is named 'places.sqlite', and can be found at "
+ "your system's profile folder. Check this link for where it is "
+ "located: http://kb.mozillazine.org/Profile_folder"
+ "\n\n\tChrome: Is named 'History', and can be found at the "
+ "respective User Data Directory. Check this link for where it is"
+ "located: https://chromium.googlesource.com/chromium/src/+/"
+ "master/docs/user_data_dir.md\n\n"
+ "Example: hist_importer.py -b firefox -s /Firefox/Profile/"
+ "places.sqlite -d /qutebrowser/data/history.sqlite")
+ parser = argparse.ArgumentParser(
+ description=description, epilog=epilog,
+ formatter_class=argparse.RawTextHelpFormatter
+ )
+ parser.add_argument('-b', '--browser', dest='browser', required=True,
+ type=str, help='Browsers: {firefox, chrome}')
+ parser.add_argument('-s', '--source', dest='source', required=True,
+ type=str, help='Source: Full path to the sqlite data'
+ 'base file from the source browser.')
+ parser.add_argument('-d', '--dest', dest='dest', required=True, type=str,
+ help='\nDestination: Full path to the qutebrowser '
+ 'sqlite database')
+ return parser.parse_args()
+
+
+def open_db(data_base):
+ """Open connection with database."""
+ if os.path.isfile(data_base):
+ return sqlite3.connect(data_base)
+ raise Error('The file {} does not exist.'.format(data_base))
+
+
+def extract(source, query):
+ """Get records from source database.
+
+ Args:
+ source: File path to the source database where we want to extract the
+ data from.
+ query: The query string to be executed in order to retrieve relevant
+ attributes as (datetime, url, time) from the source database according
+ to the browser chosen.
+ """
+ try:
+ conn = open_db(source)
+ cursor = conn.cursor()
+ cursor.execute(query)
+ history = cursor.fetchall()
+ conn.close()
+ return history
+ except sqlite3.OperationalError as op_e:
+ raise Error('Could not perform queries on the source database: '
+ '{}'.format(op_e))
+
+
+def clean(history):
+ """Clean up records from source database.
+
+ Receives a list of record and sanityze them in order for them to be
+ properly imported to qutebrowser. Sanitation requires adding a 4th
+ attribute 'redirect' which is filled with '0's, and also purging all
+ records that have a NULL/None datetime attribute.
+
+ Args:
+ history: List of records (datetime, url, title) from source database.
+ """
+ # replace missing titles with an empty string
+ for index, record in enumerate(history):
+ if record[1] is None:
+ cleaned = list(record)
+ cleaned[1] = ''
+ history[index] = tuple(cleaned)
+
+ nulls = [record for record in history if None in record]
+ for null_record in nulls:
+ history.remove(null_record)
+ history = [list(record) for record in history]
+ for record in history:
+ record.append('0')
+ return history
+
+
+def insert_qb(history, dest):
+ """Insert history into dest database.
+
+ Args:
+ history: List of records.
+ dest: File path to the destination database, where history will be
+ inserted.
+ """
+ conn = open_db(dest)
+ cursor = conn.cursor()
+ cursor.executemany(
+ 'INSERT INTO History (url,title,atime,redirect) VALUES (?,?,?,?)',
+ history
+ )
+ cursor.execute('DROP TABLE CompletionHistory')
+ conn.commit()
+ conn.close()
+
+
+def run():
+ """Main control flux of the script."""
+ args = parse()
+ browser = args.browser.lower()
+ source, dest = args.source, args.dest
+ query = {
+ 'firefox': 'select url,title,last_visit_date/1000000 as date '
+ 'from moz_places where url like "http%" or url '
+ 'like "ftp%" or url like "file://%"',
+ 'chrome': 'select url,title,last_visit_time/10000000 as date '
+ 'from urls',
+ }
+ if browser not in query:
+ raise Error('Sorry, the selected browser: "{}" is not '
+ 'supported.'.format(browser))
+ else:
+ history = extract(source, query[browser])
+ history = clean(history)
+ insert_qb(history, dest)
+
+
+def main():
+ try:
+ run()
+ except Error as e:
+ sys.exit(str(e))
+
+
+if __name__ == "__main__":
+ main()
diff --git a/.config/qutebrowser/scripts/hostblock_blame.py b/.config/qutebrowser/scripts/hostblock_blame.py
new file mode 100755
index 0000000..2f68d29
--- /dev/null
+++ b/.config/qutebrowser/scripts/hostblock_blame.py
@@ -0,0 +1,55 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Check by which hostblock list a host was blocked."""
+
+import sys
+import io
+import os
+import os.path
+import urllib.request
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
+from qutebrowser.browser import adblock
+from qutebrowser.config import configdata
+
+
+def main():
+ """Check by which hostblock list a host was blocked."""
+ if len(sys.argv) != 2:
+ print("Usage: {} <host>".format(sys.argv[0]), file=sys.stderr)
+ sys.exit(1)
+
+ configdata.init()
+
+ for url in configdata.DATA['content.host_blocking.lists'].default:
+ print("checking {}...".format(url))
+ raw_file = urllib.request.urlopen(url)
+ byte_io = io.BytesIO(raw_file.read())
+ f = adblock.get_fileobj(byte_io)
+ for line in f:
+ line = line.decode('utf-8')
+ if sys.argv[1] in line:
+ print("FOUND {} in {}:".format(sys.argv[1], url))
+ print(" " + line.rstrip())
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/importer.py b/.config/qutebrowser/scripts/importer.py
new file mode 100755
index 0000000..eb808a6
--- /dev/null
+++ b/.config/qutebrowser/scripts/importer.py
@@ -0,0 +1,349 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 Claude (longneck) <longneck@scratchbook.ch>
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+
+"""Tool to import data from other browsers.
+
+Currently importing bookmarks from Netscape Bookmark files and Mozilla
+profiles is supported.
+"""
+
+
+import argparse
+import sqlite3
+import os
+import urllib.parse
+import json
+import string
+
+browser_default_input_format = {
+ 'chromium': 'chrome',
+ 'chrome': 'chrome',
+ 'ie': 'netscape',
+ 'firefox': 'mozilla',
+ 'seamonkey': 'mozilla',
+ 'palemoon': 'mozilla',
+}
+
+
+def main():
+ args = get_args()
+ bookmark_types = []
+ output_format = None
+ input_format = args.input_format
+ if args.search_output:
+ bookmark_types = ['search']
+ if args.oldconfig:
+ output_format = 'oldsearch'
+ else:
+ output_format = 'search'
+ else:
+ if args.bookmark_output:
+ output_format = 'bookmark'
+ elif args.quickmark_output:
+ output_format = 'quickmark'
+ if args.import_bookmarks:
+ bookmark_types.append('bookmark')
+ if args.import_keywords:
+ bookmark_types.append('keyword')
+ if not bookmark_types:
+ bookmark_types = ['bookmark', 'keyword']
+ if not output_format:
+ output_format = 'quickmark'
+ if not input_format:
+ if args.browser:
+ input_format = browser_default_input_format[args.browser]
+ else:
+ #default to netscape
+ input_format = 'netscape'
+
+ import_function = {
+ 'netscape': import_netscape_bookmarks,
+ 'mozilla': import_moz_places,
+ 'chrome': import_chrome,
+ }
+ import_function[input_format](args.bookmarks, bookmark_types,
+ output_format)
+
+
+def get_args():
+ """Get the argparse parser."""
+ parser = argparse.ArgumentParser(
+ epilog="To import bookmarks from Chromium, Firefox or IE, "
+ "export them to HTML in your browsers bookmark manager. ")
+ parser.add_argument(
+ 'browser',
+ help="Which browser? {%(choices)s}",
+ choices=browser_default_input_format.keys(),
+ nargs='?',
+ metavar='browser')
+ parser.add_argument(
+ '-i',
+ '--input-format',
+ help='Which input format? (overrides browser default; "netscape" if '
+ 'neither given)',
+ choices=set(browser_default_input_format.values()),
+ required=False)
+ parser.add_argument(
+ '-b',
+ '--bookmark-output',
+ help="Output in bookmark format.",
+ action='store_true',
+ default=False,
+ required=False)
+ parser.add_argument(
+ '-q',
+ '--quickmark-output',
+ help="Output in quickmark format (default).",
+ action='store_true',
+ default=False,
+ required=False)
+ parser.add_argument(
+ '-s',
+ '--search-output',
+ help="Output config.py search engine format (negates -B and -K)",
+ action='store_true',
+ default=False,
+ required=False)
+ parser.add_argument(
+ '--oldconfig',
+ help="Output search engine format for old qutebrowser.conf format",
+ default=False,
+ action='store_true',
+ required=False)
+ parser.add_argument(
+ '-B',
+ '--import-bookmarks',
+ help="Import plain bookmarks (can be combiend with -K)",
+ action='store_true',
+ default=False,
+ required=False)
+ parser.add_argument(
+ '-K',
+ '--import-keywords',
+ help="Import keywords (can be combined with -B)",
+ action='store_true',
+ default=False,
+ required=False)
+ parser.add_argument(
+ 'bookmarks',
+ help="Bookmarks file (html format) or "
+ "profile folder (Mozilla format)")
+ args = parser.parse_args()
+ return args
+
+
+def search_escape(url):
+ """Escape URLs such that preexisting { and } are handled properly.
+
+ Will obviously trash a properly-formatted qutebrowser URL.
+ """
+ return url.replace('{', '{{').replace('}', '}}')
+
+
+def opensearch_convert(url):
+ """Convert a basic OpenSearch URL into something qutebrowser can use.
+
+ Exceptions:
+ KeyError:
+ An unknown and required parameter is present in the URL. This
+ usually means there's browser/addon specific functionality needed
+ to build the URL (I'm looking at you and your browser, Google) that
+ obviously won't be present here.
+ """
+ subst = {
+ 'searchTerms': '%s', # for proper escaping later
+ 'language': '*',
+ 'inputEncoding': 'UTF-8',
+ 'outputEncoding': 'UTF-8'
+ }
+
+ # remove optional parameters (even those we don't support)
+ for param in string.Formatter().parse(url):
+ if param[1]:
+ if param[1].endswith('?'):
+ url = url.replace('{' + param[1] + '}', '')
+ elif param[2] and param[2].endswith('?'):
+ url = url.replace('{' + param[1] + ':' + param[2] + '}', '')
+ return search_escape(url.format(**subst)).replace('%s', '{}')
+
+
+def import_netscape_bookmarks(bookmarks_file, bookmark_types, output_format):
+ """Import bookmarks from a NETSCAPE-Bookmark-file v1.
+
+ Generated by Chromium, Firefox, IE and possibly more browsers. Not all
+ export all possible bookmark types:
+ - Firefox mostly works with everything
+ - Chrome doesn't support keywords at all; searches are a separate
+ database
+ """
+ import bs4
+ with open(bookmarks_file, encoding='utf-8') as f:
+ soup = bs4.BeautifulSoup(f, 'html.parser')
+ bookmark_query = {
+ 'search': lambda tag: (
+ (tag.name == 'a') and
+ ('shortcuturl' in tag.attrs) and
+ ('%s' in tag['href'])),
+ 'keyword': lambda tag: (
+ (tag.name == 'a') and
+ ('shortcuturl' in tag.attrs) and
+ ('%s' not in tag['href'])),
+ 'bookmark': lambda tag: (
+ (tag.name == 'a') and
+ ('shortcuturl' not in tag.attrs) and
+ (tag.string)),
+ }
+ output_template = {
+ 'search': {
+ 'search':
+ "c.url.searchengines['{tag[shortcuturl]}'] = "
+ "'{tag[href]}' #{tag.string}"
+ },
+ 'oldsearch': {
+ 'search': '{tag[shortcuturl]} = {tag[href]} #{tag.string}',
+ },
+ 'bookmark': {
+ 'bookmark': '{tag[href]} {tag.string}',
+ 'keyword': '{tag[href]} {tag.string}'
+ },
+ 'quickmark': {
+ 'bookmark': '{tag.string} {tag[href]}',
+ 'keyword': '{tag[shortcuturl]} {tag[href]}'
+ }
+ }
+ bookmarks = []
+ for typ in bookmark_types:
+ tags = soup.findAll(bookmark_query[typ])
+ for tag in tags:
+ if typ == 'search':
+ tag['href'] = search_escape(tag['href']).replace('%s', '{}')
+ if tag['href'] not in bookmarks:
+ bookmarks.append(
+ output_template[output_format][typ].format(tag=tag))
+ for bookmark in bookmarks:
+ print(bookmark)
+
+
+def import_moz_places(profile, bookmark_types, output_format):
+ """Import bookmarks from a Mozilla profile's places.sqlite database."""
+ place_query = {
+ 'bookmark': (
+ "SELECT DISTINCT moz_bookmarks.title,moz_places.url "
+ "FROM moz_bookmarks,moz_places "
+ "WHERE moz_places.id=moz_bookmarks.fk "
+ "AND moz_places.id NOT IN (SELECT place_id FROM moz_keywords) "
+ "AND moz_places.url NOT LIKE 'place:%';"
+ ), # Bookmarks with no keywords assigned
+ 'keyword': (
+ "SELECT moz_keywords.keyword,moz_places.url "
+ "FROM moz_keywords,moz_places,moz_bookmarks "
+ "WHERE moz_places.id=moz_bookmarks.fk "
+ "AND moz_places.id=moz_keywords.place_id "
+ "AND moz_places.url NOT LIKE '%!%s%' ESCAPE '!';"
+ ), # Bookmarks with keywords assigned but no %s substitution
+ 'search': (
+ "SELECT moz_keywords.keyword, "
+ " moz_bookmarks.title, "
+ " search_conv(moz_places.url) AS url "
+ "FROM moz_keywords,moz_places,moz_bookmarks "
+ "WHERE moz_places.id=moz_bookmarks.fk "
+ "AND moz_places.id=moz_keywords.place_id "
+ "AND moz_places.url LIKE '%!%s%' ESCAPE '!';"
+ ) # bookmarks with keyword and %s substitution
+ }
+ out_template = {
+ 'bookmark': {
+ 'bookmark': '{url} {title}',
+ 'keyword': '{url} {keyword}'
+ },
+ 'quickmark': {
+ 'bookmark': '{title} {url}',
+ 'keyword': '{keyword} {url}'
+ },
+ 'oldsearch': {
+ 'search': '{keyword} {url} #{title}'
+ },
+ 'search': {
+ 'search': "c.url.searchengines['{keyword}'] = '{url}' #{title}"
+ }
+ }
+
+ def search_conv(url):
+ return search_escape(url).replace('%s', '{}')
+
+ places = sqlite3.connect(os.path.join(profile, "places.sqlite"))
+ places.create_function('search_conv', 1, search_conv)
+ places.row_factory = sqlite3.Row
+ c = places.cursor()
+ for typ in bookmark_types:
+ c.execute(place_query[typ])
+ for row in c:
+ print(out_template[output_format][typ].format(**row))
+
+
+def import_chrome(profile, bookmark_types, output_format):
+ """Import bookmarks and search keywords from Chrome-type profiles.
+
+ On Chrome, keywords and search engines are the same thing and handled in
+ their own database table; bookmarks cannot have associated keywords. This
+ is why the dictionary lookups here are much simpler.
+ """
+ out_template = {
+ 'bookmark': '{url} {name}',
+ 'quickmark': '{name} {url}',
+ 'search': "c.url.searchengines['{keyword}'] = '{url}'",
+ 'oldsearch': '{keyword} {url}'
+ }
+
+ if 'search' in bookmark_types:
+ webdata = sqlite3.connect(os.path.join(profile, 'Web Data'))
+ c = webdata.cursor()
+ c.execute('SELECT keyword,url FROM keywords;')
+ for keyword, url in c:
+ try:
+ url = opensearch_convert(url)
+ print(out_template[output_format].format(
+ keyword=keyword, url=url))
+ except KeyError:
+ print('# Unsupported parameter in url for {}; skipping....'.
+ format(keyword))
+
+ else:
+ with open(os.path.join(profile, 'Bookmarks'), encoding='utf-8') as f:
+ bookmarks = json.load(f)
+
+ def bm_tree_walk(bm, template):
+ """Recursive function to walk through bookmarks."""
+ assert 'type' in bm, bm
+ if bm['type'] == 'url':
+ if urllib.parse.urlparse(bm['url']).scheme != 'chrome':
+ print(template.format(**bm))
+ elif bm['type'] == 'folder':
+ for child in bm['children']:
+ bm_tree_walk(child, template)
+
+ for root in bookmarks['roots'].values():
+ bm_tree_walk(root, out_template[output_format])
+
+
+if __name__ == '__main__':
+ main()
diff --git a/.config/qutebrowser/scripts/keytester.py b/.config/qutebrowser/scripts/keytester.py
new file mode 100755
index 0000000..ee5eb34
--- /dev/null
+++ b/.config/qutebrowser/scripts/keytester.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Small test script to show key presses.
+
+Use python3 -m scripts.keytester to launch it.
+"""
+
+from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QHBoxLayout
+
+from qutebrowser.keyinput import keyutils
+
+
+class KeyWidget(QWidget):
+
+ """Widget displaying key presses."""
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self._layout = QHBoxLayout(self)
+ self._label = QLabel(text="Waiting for keypress...")
+ self._layout.addWidget(self._label)
+
+ def keyPressEvent(self, e):
+ """Show pressed keys."""
+ lines = [
+ str(keyutils.KeyInfo.from_event(e)),
+ '',
+ 'key: 0x{:x}'.format(int(e.key())),
+ 'modifiers: 0x{:x}'.format(int(e.modifiers())),
+ 'text: {!r}'.format(e.text()),
+ ]
+ self._label.setText('\n'.join(lines))
+
+
+app = QApplication([])
+w = KeyWidget()
+w.show()
+app.exec_()
diff --git a/.config/qutebrowser/scripts/link_pyqt.py b/.config/qutebrowser/scripts/link_pyqt.py
new file mode 100755
index 0000000..ae7eaa6
--- /dev/null
+++ b/.config/qutebrowser/scripts/link_pyqt.py
@@ -0,0 +1,233 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Symlink PyQt into a given virtualenv."""
+
+import os
+import os.path
+import argparse
+import shutil
+import sys
+import subprocess
+import tempfile
+import filecmp
+
+
+class Error(Exception):
+
+ """Exception raised when linking fails."""
+
+ pass
+
+
+def run_py(executable, *code):
+ """Run the given python code with the given executable."""
+ if os.name == 'nt' and len(code) > 1:
+ # Windows can't do newlines in arguments...
+ oshandle, filename = tempfile.mkstemp()
+ with os.fdopen(oshandle, 'w') as f:
+ f.write('\n'.join(code))
+ cmd = [executable, filename]
+ try:
+ ret = subprocess.run(cmd, universal_newlines=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,
+ stdout=subprocess.PIPE).stdout
+ return ret.rstrip()
+
+
+def verbose_copy(src, dst, *, follow_symlinks=True):
+ """Copy function for shutil.copytree which prints copied files."""
+ if '-v' in sys.argv:
+ print('{} -> {}'.format(src, dst))
+ shutil.copy(src, dst, follow_symlinks=follow_symlinks)
+
+
+def get_ignored_files(directory, files):
+ """Get the files which should be ignored for link_pyqt() on Windows."""
+ needed_exts = ('.py', '.dll', '.pyd', '.so')
+ ignored_dirs = ('examples', 'qml', 'uic', 'doc')
+ filtered = []
+ for f in files:
+ ext = os.path.splitext(f)[1]
+ full_path = os.path.join(directory, f)
+ if os.path.isdir(full_path) and f in ignored_dirs:
+ filtered.append(f)
+ elif (ext not in needed_exts) and os.path.isfile(full_path):
+ filtered.append(f)
+ return filtered
+
+
+def needs_update(source, dest):
+ """Check if a file to be linked/copied needs to be updated."""
+ if os.path.islink(dest):
+ # No need to delete a link and relink -> skip this
+ return False
+ elif os.path.isdir(dest):
+ diffs = filecmp.dircmp(source, dest)
+ ignored = get_ignored_files(source, diffs.left_only)
+ has_new_files = set(ignored) != set(diffs.left_only)
+ return (has_new_files or diffs.right_only or diffs.common_funny or
+ diffs.diff_files or diffs.funny_files)
+ else:
+ return not filecmp.cmp(source, dest)
+
+
+def get_lib_path(executable, name, required=True):
+ """Get the path of a python library.
+
+ Args:
+ executable: The Python executable to use.
+ name: The name of the library to get the path for.
+ required: Whether Error should be raised if the lib was not found.
+ """
+ code = [
+ 'try:',
+ ' import {}'.format(name),
+ 'except ImportError as e:',
+ ' print("ImportError: " + str(e))',
+ 'else:',
+ ' print("path: " + {}.__file__)'.format(name)
+ ]
+ output = run_py(executable, *code)
+
+ try:
+ prefix, data = output.split(': ')
+ except ValueError:
+ raise ValueError("Unexpected output: {!r}".format(output))
+
+ if prefix == 'path':
+ return data
+ elif prefix == 'ImportError':
+ if required:
+ raise Error("Could not import {} with {}: {}!".format(
+ name, executable, data))
+ else:
+ return None
+ else:
+ raise ValueError("Unexpected output: {!r}".format(output))
+
+
+def link_pyqt(executable, venv_path):
+ """Symlink the systemwide PyQt/sip into the venv.
+
+ Args:
+ executable: The python executable where the source files are present.
+ venv_path: The path to the virtualenv site-packages.
+ """
+ try:
+ get_lib_path(executable, 'PyQt5.sip')
+ except Error:
+ # There is no PyQt5.sip, so we need to copy the toplevel sip.
+ sip_file = get_lib_path(executable, 'sip')
+ else:
+ # There is a PyQt5.sip, it'll get copied with the PyQt5 dir.
+ sip_file = None
+
+ sipconfig_file = get_lib_path(executable, 'sipconfig', required=False)
+ pyqt_dir = os.path.dirname(get_lib_path(executable, 'PyQt5.QtCore'))
+
+ for path in [sip_file, sipconfig_file, pyqt_dir]:
+ if path is None:
+ continue
+
+ fn = os.path.basename(path)
+ dest = os.path.join(venv_path, fn)
+
+ if os.path.exists(dest):
+ if needs_update(path, dest):
+ remove(dest)
+ else:
+ continue
+
+ copy_or_link(path, dest)
+
+
+def copy_or_link(source, dest):
+ """Copy or symlink source to dest."""
+ if os.name == 'nt':
+ if os.path.isdir(source):
+ print('{} -> {}'.format(source, dest))
+ shutil.copytree(source, dest, ignore=get_ignored_files,
+ copy_function=verbose_copy)
+ else:
+ print('{} -> {}'.format(source, dest))
+ shutil.copy(source, dest)
+ else:
+ print('{} -> {}'.format(source, dest))
+ os.symlink(source, dest)
+
+
+def remove(filename):
+ """Remove a given filename, regardless of whether it's a file or dir."""
+ if os.path.isdir(filename):
+ shutil.rmtree(filename)
+ else:
+ os.unlink(filename)
+
+
+def get_venv_lib_path(path):
+ """Get the library path of a virtualenv."""
+ subdir = 'Scripts' if os.name == 'nt' else 'bin'
+ executable = os.path.join(path, subdir, 'python')
+ return run_py(executable,
+ 'from distutils.sysconfig import get_python_lib',
+ 'print(get_python_lib())')
+
+
+def get_tox_syspython(tox_path):
+ """Get the system python based on a virtualenv created by tox."""
+ path = os.path.join(tox_path, '.tox-config1')
+ with open(path, encoding='ascii') as f:
+ line = f.readline()
+ _md5, sys_python = line.rstrip().split(' ', 1)
+ return sys_python
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('path', help="Base path to the venv.")
+ parser.add_argument('--tox', help="Add when called via tox.",
+ action='store_true')
+ args = parser.parse_args()
+
+ if args.tox:
+ # Workaround for the lack of negative factors in tox.ini
+ if 'LINK_PYQT_SKIP' in os.environ:
+ print('LINK_PYQT_SKIP set, exiting...')
+ sys.exit(0)
+ executable = get_tox_syspython(args.path)
+ else:
+ executable = sys.executable
+
+ venv_path = get_venv_lib_path(args.path)
+ link_pyqt(executable, venv_path)
+
+
+if __name__ == '__main__':
+ try:
+ main()
+ except Error as e:
+ print(str(e), file=sys.stderr)
+ sys.exit(1)
diff --git a/.config/qutebrowser/scripts/open_url_in_instance.sh b/.config/qutebrowser/scripts/open_url_in_instance.sh
new file mode 100755
index 0000000..a6ce0ed
--- /dev/null
+++ b/.config/qutebrowser/scripts/open_url_in_instance.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# initial idea: Florian Bruhin (The-Compiler)
+# author: Thore Bödecker (foxxx0)
+
+_url="$1"
+_qb_version='1.0.4'
+_proto_version=1
+_ipc_socket="${XDG_RUNTIME_DIR}/qutebrowser/ipc-$(echo -n "$USER" | md5sum | cut -d' ' -f1)"
+_qute_bin="/usr/bin/qutebrowser"
+
+printf '{"args": ["%s"], "target_arg": null, "version": "%s", "protocol_version": %d, "cwd": "%s"}\n' \
+ "${_url}" \
+ "${_qb_version}" \
+ "${_proto_version}" \
+ "${PWD}" | socat - UNIX-CONNECT:"${_ipc_socket}" 2>/dev/null || "$_qute_bin" "$@" &
diff --git a/.config/qutebrowser/scripts/setupcommon.py b/.config/qutebrowser/scripts/setupcommon.py
new file mode 100755
index 0000000..50eabac
--- /dev/null
+++ b/.config/qutebrowser/scripts/setupcommon.py
@@ -0,0 +1,74 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+
+"""Data used by setup.py and the PyInstaller qutebrowser.spec."""
+
+import sys
+import os
+import os.path
+import subprocess
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir))
+
+
+if sys.hexversion >= 0x03000000:
+ open_file = open
+else:
+ import codecs
+ open_file = codecs.open
+
+
+BASEDIR = os.path.join(os.path.dirname(os.path.realpath(__file__)),
+ os.path.pardir)
+
+
+def _git_str():
+ """Try to find out git version.
+
+ Return:
+ string containing the git commit ID and timestamp.
+ None if there was an error or we're not in a git repo.
+ """
+ if BASEDIR is None:
+ return None
+ if not os.path.isdir(os.path.join(BASEDIR, ".git")):
+ return None
+ try:
+ # https://stackoverflow.com/questions/21017300/21017394#21017394
+ commit_hash = subprocess.run(
+ ['git', 'describe', '--match=NeVeRmAtCh', '--always', '--dirty'],
+ cwd=BASEDIR, check=True,
+ stdout=subprocess.PIPE).stdout.decode('UTF-8').strip()
+ date = subprocess.run(
+ ['git', 'show', '-s', '--format=%ci', 'HEAD'],
+ cwd=BASEDIR, check=True,
+ stdout=subprocess.PIPE).stdout.decode('UTF-8').strip()
+ return '{} ({})'.format(commit_hash, date)
+ except (subprocess.CalledProcessError, OSError):
+ return None
+
+
+def write_git_file():
+ """Write the git-commit-id file with the current commit."""
+ gitstr = _git_str()
+ if gitstr is None:
+ gitstr = ''
+ path = os.path.join(BASEDIR, 'qutebrowser', 'git-commit-id')
+ with open_file(path, 'w', encoding='ascii') as f:
+ f.write(gitstr)
diff --git a/.config/qutebrowser/scripts/testbrowser/cpp/webengine/main.cpp b/.config/qutebrowser/scripts/testbrowser/cpp/webengine/main.cpp
new file mode 100644
index 0000000..311432e
--- /dev/null
+++ b/.config/qutebrowser/scripts/testbrowser/cpp/webengine/main.cpp
@@ -0,0 +1,13 @@
+#include <QApplication>
+#include <QWebEngineView>
+#include <QUrl>
+
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QWebEngineView view;
+ view.load(QUrl(argv[1]));
+ view.show();
+ return app.exec();
+}
diff --git a/.config/qutebrowser/scripts/testbrowser/cpp/webengine/testbrowser.pro b/.config/qutebrowser/scripts/testbrowser/cpp/webengine/testbrowser.pro
new file mode 100644
index 0000000..12a1cf7
--- /dev/null
+++ b/.config/qutebrowser/scripts/testbrowser/cpp/webengine/testbrowser.pro
@@ -0,0 +1,6 @@
+QT += core widgets webenginewidgets
+
+TARGET = testbrowser
+TEMPLATE = app
+
+SOURCES += main.cpp
diff --git a/.config/qutebrowser/scripts/testbrowser/cpp/webkit/main.cpp b/.config/qutebrowser/scripts/testbrowser/cpp/webkit/main.cpp
new file mode 100644
index 0000000..06c3d1a
--- /dev/null
+++ b/.config/qutebrowser/scripts/testbrowser/cpp/webkit/main.cpp
@@ -0,0 +1,13 @@
+#include <QApplication>
+#include <QWebView>
+#include <QUrl>
+
+
+int main(int argc, char *argv[])
+{
+ QApplication app(argc, argv);
+ QWebView view;
+ view.load(QUrl(argv[1]));
+ view.show();
+ return app.exec();
+}
diff --git a/.config/qutebrowser/scripts/testbrowser/cpp/webkit/testbrowser.pro b/.config/qutebrowser/scripts/testbrowser/cpp/webkit/testbrowser.pro
new file mode 100644
index 0000000..59f55dd
--- /dev/null
+++ b/.config/qutebrowser/scripts/testbrowser/cpp/webkit/testbrowser.pro
@@ -0,0 +1,6 @@
+QT += core widgets webkit webkitwidgets
+
+TARGET = testbrowser
+TEMPLATE = app
+
+SOURCES += main.cpp
diff --git a/.config/qutebrowser/scripts/testbrowser/testbrowser_webengine.py b/.config/qutebrowser/scripts/testbrowser/testbrowser_webengine.py
new file mode 100755
index 0000000..fdf6728
--- /dev/null
+++ b/.config/qutebrowser/scripts/testbrowser/testbrowser_webengine.py
@@ -0,0 +1,50 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Very simple browser for testing purposes."""
+
+import sys
+import argparse
+
+from PyQt5.QtCore import QUrl
+from PyQt5.QtWidgets import QApplication
+from PyQt5.QtWebEngineWidgets import QWebEngineView
+
+
+def parse_args():
+ """Parse commandline arguments."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument('url', help='The URL to open')
+ return parser.parse_known_args()[0]
+
+
+if __name__ == '__main__':
+ args = parse_args()
+ app = QApplication(sys.argv)
+ wv = QWebEngineView()
+
+ wv.loadStarted.connect(lambda: print("Loading started"))
+ wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p)))
+ wv.loadFinished.connect(lambda: print("Loading finished"))
+
+ wv.load(QUrl.fromUserInput(args.url))
+ wv.show()
+
+ app.exec_()
diff --git a/.config/qutebrowser/scripts/testbrowser/testbrowser_webkit.py b/.config/qutebrowser/scripts/testbrowser/testbrowser_webkit.py
new file mode 100755
index 0000000..73cae08
--- /dev/null
+++ b/.config/qutebrowser/scripts/testbrowser/testbrowser_webkit.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python3
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Very simple browser for testing purposes."""
+
+import sys
+import argparse
+
+from PyQt5.QtCore import QUrl
+from PyQt5.QtWidgets import QApplication
+from PyQt5.QtWebKit import QWebSettings
+from PyQt5.QtWebKitWidgets import QWebView
+
+
+def parse_args():
+ """Parse commandline arguments."""
+ parser = argparse.ArgumentParser()
+ parser.add_argument('url', help='The URL to open')
+ parser.add_argument('--plugins', '-p', help='Enable plugins',
+ default=False, action='store_true')
+ return parser.parse_known_args()[0]
+
+
+if __name__ == '__main__':
+ args = parse_args()
+ app = QApplication(sys.argv)
+ wv = QWebView()
+
+ wv.loadStarted.connect(lambda: print("Loading started"))
+ wv.loadProgress.connect(lambda p: print("Loading progress: {}%".format(p)))
+ wv.loadFinished.connect(lambda: print("Loading finished"))
+
+ if args.plugins:
+ wv.settings().setAttribute(QWebSettings.PluginsEnabled, True)
+
+ wv.load(QUrl.fromUserInput(args.url))
+ wv.show()
+
+ app.exec_()
diff --git a/.config/qutebrowser/scripts/utils.py b/.config/qutebrowser/scripts/utils.py
new file mode 100755
index 0000000..9a1a751
--- /dev/null
+++ b/.config/qutebrowser/scripts/utils.py
@@ -0,0 +1,103 @@
+# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et:
+
+# Copyright 2014-2018 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 <http://www.gnu.org/licenses/>.
+
+"""Utility functions for scripts."""
+
+import os
+import os.path
+
+
+# Import side-effects are an evil thing, but here it's okay so scripts using
+# colors work on Windows as well.
+try:
+ import colorama
+except ImportError:
+ colorama = None
+else:
+ colorama.init()
+
+
+use_color = os.name != 'nt' or colorama
+
+
+fg_colors = {
+ 'black': 30,
+ 'red': 31,
+ 'green': 32,
+ 'yellow': 33,
+ 'blue': 34,
+ 'magenta': 35,
+ 'cyan': 36,
+ 'white': 37,
+ 'reset': 39,
+}
+
+
+bg_colors = {name: col + 10 for name, col in fg_colors.items()}
+
+
+term_attributes = {
+ 'bright': 1,
+ 'dim': 2,
+ 'normal': 22,
+ 'reset': 0,
+}
+
+
+def _esc(code):
+ """Get an ANSI color code based on a color number."""
+ return '\033[{}m'.format(code)
+
+
+def print_col(text, color):
+ """Print a colorized text."""
+ if use_color:
+ fg = _esc(fg_colors[color.lower()])
+ reset = _esc(fg_colors['reset'])
+ print(''.join([fg, text, reset]))
+ else:
+ print(text)
+
+
+def print_title(text):
+ """Print a title."""
+ print_col("==================== {} ====================".format(text),
+ 'yellow')
+
+
+def print_subtitle(text):
+ """Print a subtitle."""
+ print_col("------ {} ------".format(text), 'cyan')
+
+
+def print_bold(text):
+ """Print a bold text."""
+ if use_color:
+ bold = _esc(term_attributes['bright'])
+ reset = _esc(term_attributes['reset'])
+ print(''.join([bold, text, reset]))
+ else:
+ print(text)
+
+
+def change_cwd():
+ """Change the scripts cwd if it was started inside the script folder."""
+ cwd = os.getcwd()
+ if os.path.split(cwd)[1] == 'scripts':
+ os.chdir(os.path.join(cwd, os.pardir))