[packages/uzbl] - up to 2013.12.08 git snap (which doesn't hang on some analytics js)
arekm
arekm at pld-linux.org
Sun Dec 8 17:09:36 CET 2013
commit d2f1f677cbeb64f3e2b067e74398ad03d017304a
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date: Sun Dec 8 17:09:32 2013 +0100
- up to 2013.12.08 git snap (which doesn't hang on some analytics js)
uzbl-git.patch | 11204 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
uzbl.spec | 21 +-
2 files changed, 11218 insertions(+), 7 deletions(-)
---
diff --git a/uzbl.spec b/uzbl.spec
index 48c86d3..1c54b82 100644
--- a/uzbl.spec
+++ b/uzbl.spec
@@ -5,18 +5,21 @@ Summary: A keyboard controlled (modal vim-like bindings, or with modifier keys)
Summary(hu.UTF-8): Egy billentyűzettel irányítható (vim-szerű vagy módosító kódok) böngésző Webkit alapokon
Summary(pl.UTF-8): Minimalistyczna przeglądarka w całości obsługiwana przy użyciu klawiatury
Name: uzbl
-Version: 2012.05.14
+Version: 2013.12.08
Release: 1
License: GPL v3
Group: X11/Applications/Networking
# git://github.com/Dieterbe/uzbl.git
-Source0: https://github.com/Dieterbe/uzbl/archive/%{version}.tar.gz
-# Source0-md5: 8d644ecb28cb27fbc971c771156dee6f
+# Source0: https://github.com/Dieterbe/uzbl/archive/%{version}.tar.gz
+Source0: %{name}-%{version}.tar.bz2
+# Source0-md5: ff21df4ce77829ec35f3632b5232069d
+Patch0: %{name}-build.patch
URL: http://www.uzbl.org/
-BuildRequires: gtk+2-devel
-BuildRequires: gtk-webkit-devel >= 1.2.0-4
+BuildRequires: gtk+3-devel
+BuildRequires: gtk-webkit3-devel >= 1.2.0-4
BuildRequires: libsoup-devel
BuildRequires: pkgconfig
+BuildRequires: python3-devel
BuildRequires: rpm-pythonprov
Requires: %{name}-core = %{epoch}:%{version}-%{release}
Requires: dmenu
@@ -92,6 +95,7 @@ przeglądarki firefox.
%prep
%setup -q
+%patch0 -p1
%build
%{__make}
@@ -124,14 +128,17 @@ rm -rf $RPM_BUILD_ROOT
%{_datadir}/uzbl/examples/config
%dir %{_datadir}/uzbl/examples/data
%dir %{_datadir}/uzbl/examples/data/scripts
-%dir %{_datadir}/uzbl/examples/data/plugins
+#%dir %{_datadir}/uzbl/examples/data/plugins
%attr(755,root,root) %{_datadir}/uzbl/examples/data/scripts/*
-%attr(755,root,root) %{_datadir}/uzbl/examples/data/plugins/*
+#%attr(755,root,root) %{_datadir}/uzbl/examples/data/plugins/*
%{_datadir}/uzbl/examples/data/dforms
%{_datadir}/uzbl/examples/data/bookmarks
%{_datadir}/uzbl/examples/data/per-site-settings
%{_datadir}/uzbl/examples/data/uzbl.png
+%{py3_sitescriptdir}/uzbl*.egg-info
+%{py3_sitescriptdir}/uzbl
+
%files event-manager
%defattr(644,root,root,755)
%attr(755,root,root) %{_bindir}/uzbl-event-manager
diff --git a/uzbl-git.patch b/uzbl-git.patch
new file mode 100644
index 0000000..8d4610a
--- /dev/null
+++ b/uzbl-git.patch
@@ -0,0 +1,11204 @@
+diff --git a/.gitignore b/.gitignore
+index 8c08dc0..092909f 100644
+--- a/.gitignore
++++ b/.gitignore
+@@ -1,4 +1,5 @@
+ uzbl-core
++local.mk
+ *.o
+ *.lo
+ *.pyc
+@@ -6,3 +7,4 @@ uzbl-core
+ *~
+ tags
+ sandbox
++/build
+diff --git a/AUTHORS b/AUTHORS
+index b3ce2e2..22d3c9b 100644
+--- a/AUTHORS
++++ b/AUTHORS
+@@ -46,6 +46,7 @@ In alphabetical order:
+ Gregor Uhlenheuer (kongo2002) <kongo2002 at googlemail.com> - uzbl vim syntax & related files
+ Helmut Grohne (helmut) - move void **ptr to union, various fixes
+ Henri Kemppainen (DuClare) <email is akarinotengoku AT THE DOMAIN OF gmail.com> - many contributions, mostly old handler code
++ Håkan Jerning - uzbl-tabbed: autosave_session patch
+ Igor Bogomazov - mouse ptr events
+ Jake Probst <jake.probst at gmail.com> - uzbl_tabbed: multiline tablist, new window opening patches
+ James Campos (aeosynth) <james.r.campos at gmail.com> - Re-orderable gtk notebook tabs in uzbl-tabbed
+diff --git a/Makefile b/Makefile
+index a11fc8d..ba74e57 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,25 +1,53 @@
++# Create a local.mk file to store default local settings to override the
++# defaults below.
++include $(wildcard local.mk)
++
+ # packagers, set DESTDIR to your "package directory" and PREFIX to the prefix you want to have on the end-user system
+ # end-users who build from source: don't care about DESTDIR, update PREFIX if you want to
+ # RUN_PREFIX : what the prefix is when the software is run. usually the same as PREFIX
+-PREFIX?=/usr/local
+-INSTALLDIR?=$(DESTDIR)$(PREFIX)
+-DOCDIR?=$(INSTALLDIR)/share/uzbl/docs
+-RUN_PREFIX?=$(PREFIX)
++PREFIX ?= /usr/local
++INSTALLDIR ?= $(DESTDIR)$(PREFIX)
++DOCDIR ?= $(INSTALLDIR)/share/uzbl/docs
++RUN_PREFIX ?= $(PREFIX)
++
++ENABLE_WEBKIT2 ?= no
++ENABLE_GTK3 ?= auto
++
++PYTHON=python3
++PYTHONV=$(shell $(PYTHON) --version | sed -n /[0-9].[0-9]/p)
++COVERAGE=$(shell which coverage)
++
++# --- configuration ends here ---
++
++ifeq ($(ENABLE_WEBKIT2),auto)
++ENABLE_WEBKIT2 := $(shell pkg-config --exists webkit2gtk-3.0 && echo yes)
++endif
+
+-# use GTK3-based webkit when it is available
+-USE_GTK3 = $(shell pkg-config --exists gtk+-3.0 webkitgtk-3.0 && echo 1)
++ifeq ($(ENABLE_GTK3),auto)
++ENABLE_GTK3 := $(shell pkg-config --exists gtk+-3.0 && echo yes)
++endif
+
+-ifeq ($(USE_GTK3),1)
+- REQ_PKGS += gtk+-3.0 webkitgtk-3.0 javascriptcoregtk-3.0
+- CPPFLAGS = -DG_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED
++ifeq ($(ENABLE_WEBKIT2),yes)
++REQ_PKGS += 'webkit2gtk-3.0 >= 1.2.4' javascriptcoregtk-3.0
++CPPFLAGS += -DUSE_WEBKIT2
++# WebKit2 requires GTK3
++ENABLE_GTK3 := yes
++else
++ifeq ($(ENABLE_GTK3),yes)
++REQ_PKGS += 'webkitgtk-3.0 >= 1.2.4' javascriptcoregtk-3.0
+ else
+- REQ_PKGS += gtk+-2.0 webkit-1.0 javascriptcoregtk-1.0
+- CPPFLAGS =
++REQ_PKGS += 'webkit-1.0 >= 1.2.4' javascriptcoregtk-1.0
++endif
+ endif
+
+-# --- configuration ends here ---
++ifeq ($(ENABLE_GTK3),yes)
++REQ_PKGS += gtk+-3.0
++CPPFLAGS += -DG_DISABLE_DEPRECATED -DGTK_DISABLE_DEPRECATED
++else
++REQ_PKGS += gtk+-2.0
++endif
+
+-REQ_PKGS += libsoup-2.4 gthread-2.0 glib-2.0
++REQ_PKGS += 'libsoup-2.4 >= 2.30' gthread-2.0 glib-2.0
+
+ ARCH:=$(shell uname -m)
+
+@@ -33,10 +61,11 @@ LDLIBS:=$(shell pkg-config --libs $(REQ_PKGS) x11)
+
+ CFLAGS += -std=c99 $(PKG_CFLAGS) -ggdb -W -Wall -Wextra -pedantic -pthread
+
+-SRC = $(wildcard src/*.c)
++SRC = $(wildcard src/*.c)
+ HEAD = $(wildcard src/*.h)
+ OBJ = $(foreach obj, $(SRC:.c=.o), $(notdir $(obj)))
+ LOBJ = $(foreach obj, $(SRC:.c=.lo), $(notdir $(obj)))
++PY = $(wildcard uzbl/*.py uzbl/plugins/*.py)
+
+ all: uzbl-browser
+
+@@ -46,7 +75,13 @@ ${OBJ}: ${HEAD}
+
+ uzbl-core: ${OBJ}
+
+-uzbl-browser: uzbl-core
++uzbl-browser: uzbl-core uzbl-event-manager
++
++build: ${PY}
++ $(PYTHON) setup.py build
++
++.PHONY: uzbl-event-manager
++uzbl-event-manager: build
+
+ # the 'tests' target can never be up to date
+ .PHONY: tests
+@@ -61,38 +96,43 @@ tests: ${LOBJ} force
+ $(CC) -shared -Wl ${LOBJ} -o ./tests/libuzbl-core.so
+ cd ./tests/; $(MAKE)
+
++test-event-manager: force
++ ${PYTHON} -m unittest discover tests/event-manager -v
++
++coverage-event-manager: force
++ ${PYTHON} ${COVERAGE} erase
++ ${PYTHON} ${COVERAGE} run -m unittest discover tests/event-manager
++ ${PYTHON} ${COVERAGE} html ${PY}
++ # Hmm, I wonder what a good default browser would be
++ uzbl-browser htmlcov/index.html
++
+ test-uzbl-core: uzbl-core
+ ./uzbl-core --uri http://www.uzbl.org --verbose
+
+ test-uzbl-browser: uzbl-browser
+ ./bin/uzbl-browser --uri http://www.uzbl.org --verbose
+
+-test-uzbl-core-sandbox: uzbl-core
+- make DESTDIR=./sandbox RUN_PREFIX=`pwd`/sandbox/usr/local install-uzbl-core
+- make DESTDIR=./sandbox RUN_PREFIX=`pwd`/sandbox/usr/local install-example-data
+- cp -np ./misc/env.sh ./sandbox/env.sh
++test-uzbl-core-sandbox: sandbox uzbl-core sandbox-install-uzbl-core sandbox-install-example-data
+ . ./sandbox/env.sh && uzbl-core --uri http://www.uzbl.org --verbose
+ make DESTDIR=./sandbox uninstall
+ rm -rf ./sandbox/usr
+
+-test-uzbl-browser-sandbox: uzbl-browser
+- make DESTDIR=./sandbox RUN_PREFIX=`pwd`/sandbox/usr/local install-uzbl-browser
+- make DESTDIR=./sandbox RUN_PREFIX=`pwd`/sandbox/usr/local install-example-data
+- cp -np ./misc/env.sh ./sandbox/env.sh
+- -. ./sandbox/env.sh && uzbl-event-manager restart -avv
++test-uzbl-browser-sandbox: sandbox uzbl-browser sandbox-install-uzbl-browser sandbox-install-example-data
++ . ./sandbox/env.sh && ${PYTHON} -S `which uzbl-event-manager` restart -navv &
+ . ./sandbox/env.sh && uzbl-browser --uri http://www.uzbl.org --verbose
+- . ./sandbox/env.sh && uzbl-event-manager stop -ivv
++ . ./sandbox/env.sh && ${PYTHON} -S `which uzbl-event-manager` stop -vv -o /dev/null
+ make DESTDIR=./sandbox uninstall
+ rm -rf ./sandbox/usr
+
+-test-uzbl-tabbed-sandbox: uzbl-browser
+- make DESTDIR=./sandbox RUN_PREFIX=`pwd`/sandbox/usr/local install-uzbl-browser
+- make DESTDIR=./sandbox RUN_PREFIX=`pwd`/sandbox/usr/local install-uzbl-tabbed
+- make DESTDIR=./sandbox RUN_PREFIX=`pwd`/sandbox/usr/local install-example-data
+- cp -np ./misc/env.sh ./sandbox/env.sh
+- -. ./sandbox/env.sh && uzbl-event-manager restart -avv
++test-uzbl-tabbed-sandbox: sandbox uzbl-browser sandbox-install-uzbl-browser sandbox-install-uzbl-tabbed sandbox-install-example-data
++ . ./sandbox/env.sh && ${PYTHON} -S `which uzbl-event-manager` restart -avv
+ . ./sandbox/env.sh && uzbl-tabbed
+- . ./sandbox/env.sh && uzbl-event-manager stop -ivv
++ . ./sandbox/env.sh && ${PYTHON} -S `which uzbl-event-manager` stop -avv
++ make DESTDIR=./sandbox uninstall
++ rm -rf ./sandbox/usr
++
++test-uzbl-event-manager-sandbox: sandbox uzbl-browser sandbox-install-uzbl-browser sandbox-install-example-data
++ . ./sandbox/env.sh && ${PYTHON} -S `which uzbl-event-manager` restart -navv
+ make DESTDIR=./sandbox uninstall
+ rm -rf ./sandbox/usr
+
+@@ -102,18 +142,44 @@ clean:
+ find ./examples/ -name "*.pyc" -delete
+ cd ./tests/; $(MAKE) clean
+ rm -rf ./sandbox/
++ $(PYTHON) setup.py clean
+
+ strip:
+ @echo Stripping binary
+ @strip uzbl-core
+ @echo ... done.
+
++SANDBOXOPTS=\
++ DESTDIR=./sandbox\
++ RUN_PREFIX=`pwd`/sandbox/usr/local\
++ PYINSTALL_EXTRA='--prefix=./sandbox/usr/local --install-scripts=./sandbox/usr/local/bin'
++
++sandbox: misc/env.sh
++ mkdir -p sandbox/${PREFIX}/lib64
++ cp -p misc/env.sh sandbox/env.sh
++ test -e sandbox/${PREFIX}/lib || ln -s lib64 sandbox/${PREFIX}/lib
++
++sandbox-install-uzbl-browser:
++ make ${SANDBOXOPTS} install-uzbl-browser
++
++sandbox-install-uzbl-tabbed:
++ make ${SANDBOXOPTS} install-uzbl-tabbed
++
++sandbox-install-uzbl-core:
++ make ${SANDBOXOPTS} install-uzbl-core
++
++sandbox-install-event-manager:
++ make ${SANDBOXOPTS} install-event-manager
++
++sandbox-install-example-data:
++ make ${SANDBOXOPTS} install-example-data
++
+ install: install-uzbl-core install-uzbl-browser install-uzbl-tabbed
+
+ install-dirs:
+ [ -d "$(INSTALLDIR)/bin" ] || install -d -m755 $(INSTALLDIR)/bin
+
+-install-uzbl-core: all install-dirs
++install-uzbl-core: uzbl-core install-dirs
+ install -d $(INSTALLDIR)/share/uzbl/
+ install -d $(DOCDIR)
+ install -m644 docs/* $(DOCDIR)/
+@@ -123,8 +189,7 @@ install-uzbl-core: all install-dirs
+ install -m755 uzbl-core $(INSTALLDIR)/bin/uzbl-core
+
+ install-event-manager: install-dirs
+- sed "s#^PREFIX = .*#PREFIX = '$(RUN_PREFIX)'#" < bin/uzbl-event-manager > $(INSTALLDIR)/bin/uzbl-event-manager
+- chmod 755 $(INSTALLDIR)/bin/uzbl-event-manager
++ $(PYTHON) setup.py install --prefix=$(PREFIX) --install-scripts=$(INSTALLDIR)/bin $(PYINSTALL_EXTRA)
+
+ install-uzbl-browser: install-dirs install-uzbl-core install-event-manager
+ sed 's#^PREFIX=.*#PREFIX=$(RUN_PREFIX)#' < bin/uzbl-browser > $(INSTALLDIR)/bin/uzbl-browser
+diff --git a/README b/README
+index b124fb4..5241aba 100644
+--- a/README
++++ b/README
+@@ -252,18 +252,42 @@ The following commands are recognized:
+ - Open the print dialog.
+ * `include <file>`
+ - Read contents of `<file>` and interpret as a set of `uzbl` commands.
+-* `show_inspector`
+- - Show the WebInspector
++* `inspector <show | hide | coord <x> <y> | node <node-spec>>`
++ - Control the inspector. The `coord` command coordinates are relative to the
++ viewport, not the page. The `node` subcommand requires webkitgtk >=
++ 1.3.17.
++* `spell_checker <ignore <word>... | learn <word>... | autocorrect <word> | guesses <word>>`
++ - Control the spell checker. Requires webkitgtk >= 1.5.1.
+ * `add_cookie <domain> <path> <name> <value> <scheme> <expires>`
+ - Adds a new cookie to the cookie jar
+ * `delete_cookie <domain> <path> <name> <value> [<scheme> <expires>]`
+ - Deletes a matching cookie from the cookie jar. scheme and expire time
+- is currently not considered when matching.
++ is currently not considered when matching.
+ * `clear_cookies`
+ - Clears all cookies from the cookie jar
+ * `download <uri> [<destination path>]`
+ - Starts a download using the given uri. A destination file path can be given
+ to specify where the download should be written to.
++* `auth <uniqueid> <username> <password>`
++ - Authenticate as `<username>` with `<password>` for the previously issued
++ challenge with the id `<uniqueid>`. authenticating for a invalid id or one
++ expired one has no effect.
++* `snapshot <path>`
++ - Saves an image of the visible page as a PNG to the given path. Only available
++ with webkitgtk >= 1.9.6. This is not in webkit2gtk.
++* `load <string> <uri> [<baseuri>]`
++ - Load a string as text/html with the given uri. If given, all links will be
++ assumed relative to baseuri. Requires webkit2gtk >= 1.9.90.
++* `save [<format> [<path>]]`
++ - Saves the current page to a file in a given format (currently only "mhtml"
++ is supported). Requires webkit2gtk >= 1.9.90.
++* `remove_all_db`
++ - Removes all of the web databases from the current database directory path.
++* `plugin_refresh`
++ - Refreshes the plugin database. Requires webkitgtk >= 1.3.8.
++* `plugin_toggle [<plugin name> [<plugin name>...]]`
++ - Toggles whether the plugins named as arguments are enabled. No arguments is
++ interpreted as "all plugins". Requires webkitgtk >= 1.3.8.
+
+ ### VARIABLES AND CONSTANTS
+
+@@ -283,8 +307,10 @@ file).
+
+ * `uri`: The URI of the current page. (callback: load the uri)
+ * `verbose`: Controls the verbosity printed to `stdout`.
++* `inject_text`: Inject an text string, navigating to the URI "about:blank" and
++ rendering the text string given. Only available in webkit2gtk.
+ * `inject_html`: Inject an HTML string, navigating to the URI "about:blank" and
+- rendering the HTML sting given.
++ rendering the HTML string given.
+ * `geometry`: Geometry and position of the Uzbl window. Format is
+ "<width>x<height>+<x-offset>+<y-offset>".
+ * `keycmd`: Holds the input buffer (callback: update input buffer).
+@@ -333,11 +359,16 @@ file).
+ * `useragent`: The User-Agent to send to the browser, expands variables in its
+ definition.
+ * `accept_languages`: The Accept-Language header to send with HTTP requests.
++* `transparent`: If set to 1, the background of the view will be transparent
++ (default 0).
++* `view_mode`: The view mode for webkit. One of: "windowed", "floating",
++ "fullscreen", "maximized", or "minimized". Requires webkitgtk >= 1.3.4.
+ * `zoom_level`: The factor by which elements in the page are scaled with respect
+ to their original size. Setting this will resize the currently displayed page.
+ * `zoom_type`: Whether to use "full-content" zoom (defaults to true). With
+ full-content zoom on, all page content, not just text, is zoomed. When
+- full-content zoom is off, only the text of a page is zoomed.
++ full-content zoom is off, only the text of a page is zoomed. This is
++ unavailable with webkit2gtk. Use `zoom_text_only` instead.
+ * `font_size`: The default font size.
+ * `default_font_family`: The default font family used to display text.
+ * `monospace_font_family`: The default font family used to display monospace
+@@ -349,7 +380,6 @@ file).
+ * `fantasy_font_family`: The default Fantasy font family used to display text.
+ * `monospace_size`: The default size of monospaced font (default 1).
+ * `minimum_font_size`: The minimum font size used to display text (default 1).
+-* `enable_pagecache`: Enable the webkit pagecache (it caches rendered pages for a speedup when you go back or forward in history) (default 0).
+ * `enable_plugins`: Disable embedded plugin objects (default 0).
+ * `enable_scripts`: Disable embedded scripting languages (default 0).
+ * `autoload_images`: Automatically load images (default 1).
+@@ -360,25 +390,109 @@ file).
+ `en_CA` or `pt_BR`) to be used for spell checking, separated by commas.
+ Defaults to the value returned by `gtk_get_default_language`.
+ * `enable_private`: Whether to enable private browsing mode (default 0).
++* `cookie_policy`: If set to 0, all cookies are accepted, if set to 1, all
++ cookies are rejected, and 2 rejects third party cookies (default 0).
+ * `print_backgrounds`: Print background images? (default 0).
+ * `stylesheet_uri`: Use this to override the pagelayout with a custom
+ stylesheet.
+ * `resizable_text_areas`: Whether text areas can be resized (default 0).
+ * `default_encoding`: The default text encoding (default "iso-8859-1").
+-* `current_encoding`: This can be set to force a text encoding.
++* `custom_encoding`: This can be set to force a text encoding. (Used to be
++ `current_encoding` which is now read-only).
+ * `enforce_96_dpi`: Enforce a resolution of 96 DPI (default 1).
++* `editable`: Whether the page can be edited or not (default 0).
+ * `caret_browsing`: Whether the caret is enabled in the text portion of pages
+ (default 0).
+ * `enable_cross_file_access`: Whether a page loaded from a `file://` URI can
+ access the contents of other `file://` URIs. (default 0).
+ * `follow_hint_keys`: keys for keyboard-based navigation and link
+- highlighting
++ highlighting
+ * `handle_multi_click`: If set to 1, event handlers attached to `2Button*`
+- and `3Button*` bindings will only be used instead of the default actions in
+- WebKit (default 0).
++ and `3Button*` bindings will only be used instead of the default actions in
++ WebKit (default 0).
+ * `ssl_ca_file`: File that contains CA certificates.
+ * `ssl_verify`: If set to 1, uzbl won't connect to "https" url unless it can
+ validate certificate presented by remote server against `ssl_ca_file`.
++* `enable_builtin_auth`: Enable WebKits builtin authentication handler
++* `enable_java_applet`: If set to 1, support for Java <applet> tags will be
++ enabled (default 1).
++* `enable_database`: If set to 1, support for HTML5 client-side SQL databases
++ will be enabled (default 1).
++* `enable_local_storage`: If set to 1, websites will be able to store data
++ locally (default 1).
++* `enable_pagecache`: If set to 1, uzbl will store previously visited pages for
++ faster access (the cache is local to each uzbl instance) (default 0).
++* `enable_offline_app_cache`: If set to 1, web applications may be cached locally
++ for offline use (default 1).
++* `enable_universal_file_access`: If set to 1, allow `file://` URIs to access
++ all pages (default 0).
++* `enable_hyperlink_auditing`: If set to 1, the `ping` attribute on anchors will
++ be supported (default 0).
++* `zoom_step`: The change in the zoon level when zooming (default 0.1).
++* `auto_resize_window`: If set to 1, allow web pages to change window dimensions
++ (default 0).
++* `enable_spatial_navigation`: If set to 1, the arrow keys in `Ins` mode will
++ navigate between form elements (default 0).
++* `editing_behavior`: When set to 0, emulate Mac behavior in text fields, 1
++ for Windows behavior, and 2 for *nix behavior (the default).
++* `enable_tab_cycle`: If set to 1, the `Tab` key cycles between elements on
++ the page (default 1).
++* `default_context_menu`: If set to 0, do not cause context menus to appear when
++ right clicking (default 1).
++* `enable_site_workarounds`: If set to 1, enable filters to help unbreak
++ certain websites (default 0).
++* `javascript_clipboard`: If set to 1, JavaScript may access the clipboard
++ (default 0). Requires webkitgtk >= 1.3.0.
++* `javascript_dom_paste`: If set to 1, JavaScript will able to paste from the
++ clipboard (default 0).
++* `enable_frame_flattening`: If set to 1, flatten all frames into a single
++ page to become one scrollable page (default 0). Requires webkitgtk >= 1.3.5.
++* `enable_fullscreen`: If set to 1, Mozilla-style fullscreening will be
++ available (default 0). Requires webkitgtk >= 1.3.8
++* `enable_dns_prefetch`: If set to 1, domain names will be prefetched
++ (default 1). Private browsing does *not* affect this value. Requires
++ webkitgtk >= 1.3.13.
++* `display_insecure_content`: If set to 1, non-HTTPS content will be displayed
++ on HTTPS pages (default 1). Requires webkitgtk >= 1.11.13.
++* `run_insecure_content`: If set to 1, non-HTTPS content will be allowed to run
++ on HTTPS pages (default 1). Requires webkitgtk >= 1.11.13.
++* `maintain_history`: If set to 1, the back/forward list will be kept. (default
++ 1).
++* `enable_webgl`: If set to 1, WebGL support will be enabled (default 0).
++ Requires webkitgtk >= 1.3.14.
++* `local_storage_path`: Where to store local databases (default
++ $XDG_DATA_HOME/webkit/databases/). Requires webkit >= 1.5.2.
++* `enable_webaudio`: If set to 1, allows JavaScript to generate audio
++ directly (default 0). Requires webkit >= 1.7.5.
++* `enable_3d_acceleration`: If set to 1, the GPU will be used to render
++ animations and 3D CSS transformations. Requires webkitgtk >= 1.7.90.
++* `zoom_text_only`: If set to 1, only text will be zoomed (default 0). Requires
++ webkit2gtk >= 1.7.91.
++* `enable_smooth_scrolling`: If set to 1, scrolling the page will be smoothed
++ (default 0). Requires webkitgtk >= 1.9.0.
++* `enable_inline_media`: If set to 1, inline playback of media is allowed,
++ otherwise, only full-screen playback is allowed (default 1). Requires
++ webkitgtk >= 1.9.3.
++* `require_click_to_play`: If set to 1, playback of media requires user
++ interaction before playing, otherwise, media will be allowed to autoplay
++ (default 0). Requires webkitgtk >= 1.9.3.
++* `enable_css_shaders`: If set to 1, CSS shaders will be enabled (default 0).
++ Requires webkitgtk >= 1.11.1.
++* `enable_media_stream`: If set to 1, web pages will be able to access the
++ local video and audio input devices (default 0). Requires webkitgtk >= 1.11.1.
++* `cache_model`: The cache model of webkit. Valid values:
++ "document_viewer" (no caching; low memory; usage: single local file),
++ "web_browser" (heavy caching; faster; usage: general browsing),
++ "document_browser" (moderate caching; usage: series of local files)
++ (default "web_browser").
++* `app_cache_size`: The maximum size of the application cache (in bytes)
++ (default UINT_MAX (no quota)). Changing the variable clears the cache.
++ Requires webkitgtk >= 1.3.13.
++* `web_database_directory`: The directory where web databases are stored.
++ (default is under $XDG_DATA_HOME).
++* `web_database_quota`: The default quota for web databases. (default 5MB).
++* `profile_js`: Sets whether to profile JavaScript code.
++* `profile_timeline`: Sets whether to profile the timeline.
+
+ #### Constants (not dumpable or writeable)
+
+@@ -396,6 +510,13 @@ file).
+ - overridable with cmdline arg
+ - in GtkSocket mode, this is a random number to prevent name clashes
+ * `PID`: The process ID of this Uzbl instance.
++* `current_encoding`: The current encoding of the web page.
++* `inspected_uri`: The URI that is being inspected. Requires webkitgtk >=
++ 1.3.17.
++* `app_cache_directory`: The directory webkit uses to store its cache.
++ Requires webkitgtk >= 1.3.13.
++* `plugin_list`: A JSON list of objects describing the available plugins.
++ Requires webkitgtk >= 1.3.8.
+
+ ### VARIABLE EXPANSION AND COMMAND / JAVASCRIPT SUBSTITUTION
+
+@@ -514,10 +635,10 @@ access to the following environment variables:
+ * `$UZBL_SOCKET`: The filename of the Unix socket being used, if any.
+ * `$UZBL_URI`: The URI of the current page.
+ * `$UZBL_TITLE`: The current page title.
++* `$UZBL_PRIVATE`: Set if uzbl is in "private browsing mode", unset otherwise.
+
+-Handler scripts (`download_handler`, `cookie_handler`, `scheme_handler`,
+-`request_handler`, and `authentication_handler`) are called with special
+-arguments:
++Handler scripts (`download_handler`, `scheme_handler`, and `request_handler`)
++are called with special arguments:
+
+ * download handler
+
+@@ -532,16 +653,6 @@ arguments:
+ that the file should be saved to. A download handler using WebKit's internal
+ downloader can just echo this path and exit when this argument is present.
+
+-* cookie handler
+-
+- - `$1 GET/PUT`: Whether a cookie should be sent to the server (`GET`) or
+- stored by the browser (`PUT`).
+- - `$2 scheme`: Either `http` or `https`.
+- - `$3 host`: If current page URL is `www.example.com/somepage`, this could be
+- something else than `example.com`, eg advertising from another host.
+- - `$4 path`: The request address path.
+- - `$5 data`: The cookie data. Only included for `PUT` requests.
+-
+ * scheme handler
+
+ - `$1 URI` of the page to be navigated to
+@@ -550,13 +661,6 @@ arguments:
+
+ - `$1 URI` of the resource which is being requested
+
+-* authentication handler:
+-
+- - `$1`: authentication zone unique identifier
+- - `$2`: domain part of URL that requests authentication
+- - `$3`: authentication realm
+- - `$4`: FALSE if this is the first attempt to authenticate, TRUE otherwise
+-
+ ### Formfiller.sh
+
+ Example config entries for formfiller script
+@@ -584,47 +688,33 @@ after closing the editor, it will load the data into the formfields. The temp
+ file is removed
+
+ ### HTTP/BASIC AUTHENTICATION
++HTTP auth can be handled in two different ways. Using the builtin auth dialog
++in WebKit or by dispatching the work to a external script.
+
+-You can use the authentication_handler variable to denote how http
+-authentication should be handled.
+-If this variable is:
+-
+-* not set or empty: use webkit internal auth dialog
+-* a valid handler (i.e. {sh,sync}_spawn correct_script), use this handler
+-* innvalid handler (spawn, some other command, uses script that does not
+- print anything): skip authentication.
+-
+-Example:
++To use the builtin auth dialog set `enable_builtin_auth` to 1. With this set
++you'll get a basic GTK+ prompt for username/password when trying to access a
++protected site.
+
+- set authentication_handler = sync_spawn /patch/to/your/script
++Whenever authentication is needed the `AUTHENTICATE` event will be sent, this
++is what you would use to hook up a custom authentication system. This event
++will be sent even when using the bultin dialog so remember to disable that if
++adding a dialog of your own.
+
+-Script will be executed on each authentication request.
+-It will receive four auth-related parameters:
++The `AUTHENTICATE` event has four arguments
++ * a unique identifier to be used in the exchange
++ * domain part of URL that requests authentication
++ * authentication realm
++ * the empty string for the first attempt and "retrying" for further attempts
+
+- $1 authentication zone unique identifier (may be used as 'key')
+- $2 domain part of URL that requests authentication
+- $3 authentication realm
+- $4 FALSE if this is the first attempt to authenticate, TRUE otherwise
++After this event has been sent the request is paused until credentials are
++provided. This is done using the `auth` command e.g:
+
+-Script is expected to print exactly two lines of text on stdout (that means
+-its output must contain exactly two '\n' bytes).
+-The first line contains username, the second one - password.
+-If authentication fails, script will be executed again (with $4 = TRUE).
+-Non-interactive scripts should handle this case and do not try to
+-authenticate again to avoid loops. If number of '\n' characters in scripts
+-output does not equal 2, authentication will fail.
+-That means 401 error will be displayed and uzbl won't try to authenticate anymore.
++ `auth "uniqueid" "alice" "wonderland"`
+
+-The simplest example of authentication handler script is:
++A super simple setup that will always try to authenticate with the same password
++could look like this. (assuming aliases from the example configuration)
+
+-#!/bin/sh
+-[ "$4" == "TRUE ] && exit
+-echo alice
+-echo wonderland
+-
+-This script tries to authenticate as user alice with password wonderland once
+-and never retries authentication.
+-See examples for more sofisticated, interactive authentication handler.
++ at on_event AUTHENTICATE auth "%1" "alice" "wonderland"
+
+ ### WINDOW MANAGER INTEGRATION
+
+@@ -657,11 +747,6 @@ The EM allows:
+ * Many fine-grained events (`hover_over_link`, `key_press`, `key_release`,..)
+ * See example `uzbl-event-manager`.
+
+-**Note**: Cookie events are sent in addition to (optionally) being handled by
+- the cookie handler (set by the cookie_handler var). If using a handler it will
+- take precedence before the internal state configured by (add|delete)_cookie
+- commands.
+-
+ Events have this format:
+
+ EVENT [uzbl_instance_name] EVENT_NAME event_details
+@@ -687,14 +772,18 @@ Events have this format:
+ loaded. `uri` is the URI of the page being loaded.
+ * `EVENT [uzbl_instance_name] LOAD_START uri`: A change of the page has been
+ requested. `uri` is the current URI; the one being departed.
+-* `EVENT [uzbl_instance_name] LOAD_FINISHED uri`: Loading has finished for the
++* `EVENT [uzbl_instance_name] LOAD_FINISH uri`: Loading has finished for the
+ page at `uri`.
+ * `EVENT [uzbl_instance_name] LOAD_ERROR uri reason_of_error`: The URI `uri`
+ could not be loaded for the reason described in `reason_of_error`.
+ * `EVENT [uzbl_instance_name] LOAD_PROGRESS percentage` : While the page is
+ loading, gives the `percentage` of the page that has finished loading.
++* `EVENT [uzbl_instance_name] REQUEST_QUEUED uri`: http resource gets
++ enqueued
+ * `EVENT [uzbl_instance_name] REQUEST_STARTING uri`: http resource gets
+ requested
++* `EVENT [uzbl_instance_name] REQUEST_FINISHED uri`: http resource has finished
++ loading
+ * `EVENT [uzbl_instance_name] TITLE_CHANGED title_name`: When the title of the
+ page (and hence maybe, the window title) changed. `title_name` is the new
+ title.
+@@ -743,6 +832,8 @@ Events have this format:
+ be a unix-timestamp or empty
+ * `EVENT [uzbl_instance_name] DELETE_COOKIE domain path name value scheme expire`:
+ When a cookie was deleted. arguments as ADD_COOKIE
++* `EVENT [uzbl_instance_name] AUTHENTICATE uniqueid host realm retry`: When a
++ request requires authentication. authentication is done by calling `auth`
+
+ Events/requests which the EM and its plugins listens for
+
+diff --git a/bin/uzbl-browser b/bin/uzbl-browser
+index fb9a368..4381050 100755
+--- a/bin/uzbl-browser
++++ b/bin/uzbl-browser
+@@ -67,9 +67,9 @@ fi
+ # uzbl-event-manager will exit if one is already running.
+ # we could also check if its pid file exists to avoid having to spawn it.
+ DAEMON_SOCKET="$XDG_CACHE_HOME"/uzbl/event_daemon
+-#if [ ! -f "$DAEMON_SOCKET".pid ]
+-#then
++if [ ! -f "$DAEMON_SOCKET".pid ]
++then
+ ${UZBL_EVENT_MANAGER:-uzbl-event-manager -va start}
+-#fi
++fi
+
+ exec uzbl-core "$@" ${config_file:+--config "$config_file"} --connect-socket $DAEMON_SOCKET
+diff --git a/bin/uzbl-event-manager b/bin/uzbl-event-manager
+index 56253ef..221fa73 100755
+--- a/bin/uzbl-event-manager
++++ b/bin/uzbl-event-manager
+@@ -1,1011 +1,3 @@
+-#!/usr/bin/env python2
+-
+-# Event Manager for Uzbl
+-# Copyright (c) 2009-2010, Mason Larobina <mason.larobina at gmail.com>
+-# Copyright (c) 2009, Dieter Plaetinck <dieter at plaetinck.be>
+-#
+-# This program 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.
+-#
+-# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
+-
+-'''
+-
+-E V E N T _ M A N A G E R . P Y
+-===============================
+-
+-Event manager for uzbl written in python.
+-
+-'''
+-
+-import atexit
+-import imp
+-import logging
+-import os
+-import sys
+-import time
+-import weakref
+-import re
+-import errno
+-from collections import defaultdict
+-from functools import partial
+-from glob import glob
+-from itertools import count
+-from optparse import OptionParser
+-from select import select
+-from signal import signal, SIGTERM, SIGINT, SIGKILL
+-from socket import socket, AF_UNIX, SOCK_STREAM, error as socket_error
+-from traceback import format_exc
+-
+-
+-def xdghome(key, default):
+- '''Attempts to use the environ XDG_*_HOME paths if they exist otherwise
+- use $HOME and the default path.'''
+-
+- xdgkey = "XDG_%s_HOME" % key
+- if xdgkey in os.environ.keys() and os.environ[xdgkey]:
+- return os.environ[xdgkey]
+-
+- return os.path.join(os.environ['HOME'], default)
+-
+-# `make install` will put the correct value here for your system
+-PREFIX = '/usr/local/'
+-
+-# Setup xdg paths.
+-DATA_DIR = os.path.join(xdghome('DATA', '.local/share/'), 'uzbl/')
+-CACHE_DIR = os.path.join(xdghome('CACHE', '.cache/'), 'uzbl/')
+-
+-# Define some globals.
+-SCRIPTNAME = os.path.basename(sys.argv[0])
+-
+-logger = logging.getLogger(SCRIPTNAME)
+-
+-
+-def get_exc():
+- '''Format `format_exc` for logging.'''
+- return "\n%s" % format_exc().rstrip()
+-
+-
+-def expandpath(path):
+- '''Expand and realpath paths.'''
+- return os.path.realpath(os.path.expandvars(path))
+-
+-
+-def ascii(u):
+- '''Convert unicode strings into ascii for transmission over
+- ascii-only streams/sockets/devices.'''
+- return u.encode('utf-8')
+-
+-
+-def daemonize():
+- '''Daemonize the process using the Stevens' double-fork magic.'''
+-
+- logger.info('entering daemon mode')
+-
+- try:
+- if os.fork():
+- os._exit(0)
+-
+- except OSError:
+- logger.critical('failed to daemonize', exc_info=True)
+- sys.exit(1)
+-
+- os.chdir('/')
+- os.setsid()
+- os.umask(0)
+-
+- try:
+- if os.fork():
+- os._exit(0)
+-
+- except OSError:
+- logger.critical('failed to daemonize', exc_info=True)
+- sys.exit(1)
+-
+- if sys.stdout.isatty():
+- sys.stdout.flush()
+- sys.stderr.flush()
+-
+- devnull = '/dev/null'
+- stdin = file(devnull, 'r')
+- stdout = file(devnull, 'a+')
+- stderr = file(devnull, 'a+', 0)
+-
+- os.dup2(stdin.fileno(), sys.stdin.fileno())
+- os.dup2(stdout.fileno(), sys.stdout.fileno())
+- os.dup2(stderr.fileno(), sys.stderr.fileno())
+-
+- logger.info('entered daemon mode')
+-
+-
+-def make_dirs(path):
+- '''Make all basedirs recursively as required.'''
+-
+- try:
+- dirname = os.path.dirname(path)
+- if not os.path.isdir(dirname):
+- logger.debug('creating directories %r', dirname)
+- os.makedirs(dirname)
+-
+- except OSError:
+- logger.error('failed to create directories', exc_info=True)
+-
+-
+-class EventHandler(object):
+- '''Event handler class. Used to store args and kwargs which are merged
+- come time to call the callback with the event args and kwargs.'''
+-
+- nextid = count().next
+-
+- def __init__(self, plugin, event, callback, args, kwargs):
+- self.id = self.nextid()
+- self.plugin = plugin
+- self.event = event
+- self.callback = callback
+- self.args = args
+- self.kwargs = kwargs
+-
+- def __repr__(self):
+- elems = ['id=%d' % self.id, 'event=%s' % self.event,
+- 'callback=%r' % self.callback]
+-
+- if self.args:
+- elems.append('args=%s' % repr(self.args))
+-
+- if self.kwargs:
+- elems.append('kwargs=%s' % repr(self.kwargs))
+-
+- elems.append('plugin=%s' % self.plugin.name)
+- return u'<handler(%s)>' % ', '.join(elems)
+-
+- def call(self, uzbl, *args, **kwargs):
+- '''Execute the handler function and merge argument lists.'''
+-
+- args = args + self.args
+- kwargs = dict(self.kwargs.items() + kwargs.items())
+- self.callback(uzbl, *args, **kwargs)
+-
+-
+-class Plugin(object):
+- '''Plugin module wrapper object.'''
+-
+- # Special functions exported from the Plugin instance to the
+- # plugin namespace.
+- special_functions = ['require', 'export', 'export_dict', 'connect',
+- 'connect_dict', 'logger', 'unquote', 'splitquoted']
+-
+- def __init__(self, parent, name, path, plugin):
+- self.parent = parent
+- self.name = name
+- self.path = path
+- self.plugin = plugin
+- self.logger = logging.getLogger('plugin.%s' % name)
+-
+- # Weakrefs to all handlers created by this plugin
+- self.handlers = set([])
+-
+- # Plugins init hook
+- init = getattr(plugin, 'init', None)
+- self.init = init if callable(init) else None
+-
+- # Plugins optional after hook
+- after = getattr(plugin, 'after', None)
+- self.after = after if callable(after) else None
+-
+- # Plugins optional cleanup hook
+- cleanup = getattr(plugin, 'cleanup', None)
+- self.cleanup = cleanup if callable(cleanup) else None
+-
+- assert init or after or cleanup, "missing hooks in plugin"
+-
+- # Export plugin's instance methods to plugin namespace
+- for attr in self.special_functions:
+- plugin.__dict__[attr] = getattr(self, attr)
+-
+- def __repr__(self):
+- return u'<plugin(%r)>' % self.plugin
+-
+- def export(self, uzbl, attr, obj, prepend=True):
+- '''Attach `obj` to `uzbl` instance. This is the preferred method
+- of sharing functionality, functions, data and objects between
+- plugins.
+-
+- If the object is callable you may wish to turn the callable object
+- in to a meta-instance-method by prepending `uzbl` to the call stack.
+- You can change this behaviour with the `prepend` argument.
+- '''
+-
+- assert attr not in uzbl.exports, "attr %r already exported by %r" %\
+- (attr, uzbl.exports[attr][0])
+-
+- prepend = True if prepend and callable(obj) else False
+- uzbl.__dict__[attr] = partial(obj, uzbl) if prepend else obj
+- uzbl.exports[attr] = (self, obj, prepend)
+- uzbl.logger.info('exported %r to %r by plugin %r, prepended %r',
+- obj, 'uzbl.%s' % attr, self.name, prepend)
+-
+- def export_dict(self, uzbl, exports):
+- for (attr, object) in exports.items():
+- self.export(uzbl, attr, object)
+-
+- def find_handler(self, event, callback, args, kwargs):
+- '''Check if a handler with the identical callback and arguments
+- exists and return it.'''
+-
+- # Remove dead refs
+- self.handlers -= set(filter(lambda ref: not ref(), self.handlers))
+-
+- # Find existing identical handler
+- for handler in [ref() for ref in self.handlers]:
+- if handler.event == event and handler.callback == callback \
+- and handler.args == args and handler.kwargs == kwargs:
+- return handler
+-
+- def connect(self, uzbl, event, callback, *args, **kwargs):
+- '''Create an event handler object which handles `event` events.
+-
+- Arguments passed to the connect function (`args` and `kwargs`) are
+- stored in the handler object and merged with the event arguments
+- come handler execution.
+-
+- All handler functions must behave like a `uzbl` instance-method (that
+- means `uzbl` is prepended to the callback call arguments).'''
+-
+- # Sanitise and check event name
+- event = event.upper().strip()
+- assert event and ' ' not in event
+-
+- assert callable(callback), 'callback must be callable'
+-
+- # Check if an identical handler already exists
+- handler = self.find_handler(event, callback, args, kwargs)
+- if not handler:
+- # Create a new handler
+- handler = EventHandler(self, event, callback, args, kwargs)
+- self.handlers.add(weakref.ref(handler))
+- self.logger.info('new %r', handler)
+-
+- uzbl.handlers[event].append(handler)
+- uzbl.logger.info('connected %r', handler)
+- return handler
+-
+- def connect_dict(self, uzbl, connects):
+- for (event, callback) in connects.items():
+- self.connect(uzbl, event, callback)
+-
+- def require(self, plugin):
+- '''Check that plugin with name `plugin` has been loaded. Use this to
+- ensure that your plugins dependencies have been met.'''
+-
+- assert plugin in self.parent.plugins, self.logger.critical(
+- 'plugin %r required by plugin %r', plugin, self.name)
+-
+- @classmethod
+- def unquote(cls, s):
+- '''Removes quotation marks around strings if any and interprets
+- \\-escape sequences using `string_escape`'''
+- if s and s[0] == s[-1] and s[0] in ['"', "'"]:
+- s = s[1:-1]
+- return s.encode('utf-8').decode('string_escape').decode('utf-8')
+-
+- _splitquoted = re.compile("( |\"(?:\\\\.|[^\"])*?\"|'(?:\\\\.|[^'])*?')")
+-
+- @classmethod
+- def splitquoted(cls, text):
+- '''Splits string on whitespace while respecting quotations'''
+- parts = cls._splitquoted.split(text)
+- return [cls.unquote(p) for p in parts if p.strip()]
+-
+-
+-class Uzbl(object):
+- def __init__(self, parent, child_socket):
+- self.opts = opts
+- self.parent = parent
+- self.child_socket = child_socket
+- self.child_buffer = []
+- self.time = time.time()
+- self.pid = None
+- self.name = None
+-
+- # Flag if the instance has raised the INSTANCE_START event.
+- self.instance_start = False
+-
+- # Use name "unknown" until name is discovered.
+- self.logger = logging.getLogger('uzbl-instance[]')
+-
+- # Track plugin event handlers and exported functions.
+- self.exports = {}
+- self.handlers = defaultdict(list)
+-
+- # Internal vars
+- self._depth = 0
+- self._buffer = ''
+-
+- def __repr__(self):
+- return '<uzbl(%s)>' % ', '.join([
+- 'pid=%s' % (self.pid if self.pid else "Unknown"),
+- 'name=%s' % ('%r' % self.name if self.name else "Unknown"),
+- 'uptime=%f' % (time.time() - self.time),
+- '%d exports' % len(self.exports.keys()),
+- '%d handlers' % sum([len(l) for l in self.handlers.values()])])
+-
+- def init_plugins(self):
+- '''Call the init and after hooks in all loaded plugins for this
+- instance.'''
+-
+- # Initialise each plugin with the current uzbl instance.
+- for plugin in self.parent.plugins.values():
+- if plugin.init:
+- self.logger.debug('calling %r plugin init hook', plugin.name)
+- plugin.init(self)
+-
+- # Allow plugins to use exported features of other plugins by calling an
+- # optional `after` function in the plugins namespace.
+- for plugin in self.parent.plugins.values():
+- if plugin.after:
+- self.logger.debug('calling %r plugin after hook', plugin.name)
+- plugin.after(self)
+-
+- def send(self, msg):
+- '''Send a command to the uzbl instance via the child socket
+- instance.'''
+-
+- msg = msg.strip()
+- assert self.child_socket, "socket inactive"
+-
+- if opts.print_events:
+- print ascii(u'%s<-- %s' % (' ' * self._depth, msg))
+-
+- self.child_buffer.append(ascii("%s\n" % msg))
+-
+- def do_send(self):
+- data = ''.join(self.child_buffer)
+- try:
+- bsent = self.child_socket.send(data)
+- except socket_error as e:
+- if e.errno in (errno.EAGAIN, errno.EINTR):
+- self.child_buffer = [data]
+- return
+- else:
+- self.logger.error('failed to send', exc_info=True)
+- return self.close()
+- else:
+- if bsent == 0:
+- self.logger.debug('write end of connection closed')
+- self.close()
+- elif bsent < len(data):
+- self.child_buffer = [data[bsent:]]
+- else:
+- del self.child_buffer[:]
+-
+- def read(self):
+- '''Read data from the child socket and pass lines to the parse_msg
+- function.'''
+-
+- try:
+- raw = unicode(self.child_socket.recv(8192), 'utf-8', 'ignore')
+- if not raw:
+- self.logger.debug('read null byte')
+- return self.close()
+-
+- except:
+- self.logger.error('failed to read', exc_info=True)
+- return self.close()
+-
+- lines = (self._buffer + raw).split('\n')
+- self._buffer = lines.pop()
+-
+- for line in filter(None, map(unicode.strip, lines)):
+- try:
+- self.parse_msg(line.strip())
+-
+- except:
+- self.logger.error(get_exc())
+- self.logger.error('erroneous event: %r' % line)
+-
+- def parse_msg(self, line):
+- '''Parse an incoming message from a uzbl instance. Event strings
+- will be parsed into `self.event(event, args)`.'''
+-
+- # Split by spaces (and fill missing with nulls)
+- elems = (line.split(' ', 3) + [''] * 3)[:4]
+-
+- # Ignore non-event messages.
+- if elems[0] != 'EVENT':
+- logger.info('non-event message: %r', line)
+- if opts.print_events:
+- print '--- %s' % ascii(line)
+- return
+-
+- # Check event string elements
+- (name, event, args) = elems[1:]
+- assert name and event, 'event string missing elements'
+- if not self.name:
+- self.name = name
+- self.logger = logging.getLogger('uzbl-instance%s' % name)
+- self.logger.info('found instance name %r', name)
+-
+- assert self.name == name, 'instance name mismatch'
+-
+- # Handle the event with the event handlers through the event method
+- self.event(event, args)
+-
+- def event(self, event, *args, **kargs):
+- '''Raise an event.'''
+-
+- event = event.upper()
+-
+- if not opts.daemon_mode and opts.print_events:
+- elems = [event]
+- if args:
+- elems.append(unicode(args))
+- if kargs:
+- elems.append(unicode(kargs))
+- print ascii(u'%s--> %s' % (' ' * self._depth, ' '.join(elems)))
+-
+- if event == "INSTANCE_START" and args:
+- assert not self.instance_start, 'instance already started'
+-
+- self.pid = int(args[0])
+- self.logger.info('found instance pid %r', self.pid)
+-
+- self.init_plugins()
+-
+- elif event == "INSTANCE_EXIT":
+- self.logger.info('uzbl instance exit')
+- self.close()
+-
+- if event not in self.handlers:
+- return
+-
+- for handler in self.handlers[event]:
+- self._depth += 1
+- try:
+- handler.call(self, *args, **kargs)
+-
+- except:
+- self.logger.error('error in handler', exc_info=True)
+-
+- self._depth -= 1
+-
+- def close_connection(self, child_socket):
+- '''Close child socket and delete the uzbl instance created for that
+- child socket connection.'''
+-
+- def close(self):
+- '''Close the client socket and call the plugin cleanup hooks.'''
+-
+- self.logger.debug('called close method')
+-
+- # Remove self from parent uzbls dict.
+- if self.child_socket in self.parent.uzbls:
+- self.logger.debug('removing self from uzbls list')
+- del self.parent.uzbls[self.child_socket]
+-
+- try:
+- if self.child_socket:
+- self.logger.debug('closing child socket')
+- self.child_socket.close()
+-
+- except:
+- self.logger.error('failed to close socket', exc_info=True)
+-
+- finally:
+- self.child_socket = None
+-
+- # Call plugins cleanup hooks.
+- for plugin in self.parent.plugins.values():
+- if plugin.cleanup:
+- self.logger.debug('calling %r plugin cleanup hook',
+- plugin.name)
+- plugin.cleanup(self)
+-
+- logger.info('removed %r', self)
+-
+-
+-class UzblEventDaemon(object):
+- def __init__(self):
+- self.opts = opts
+- self.server_socket = None
+- self._quit = False
+-
+- # Hold uzbl instances
+- # {child socket: Uzbl instance, ..}
+- self.uzbls = {}
+-
+- # Hold plugins
+- # {plugin name: Plugin instance, ..}
+- self.plugins = {}
+-
+- # Register that the event daemon server has started by creating the
+- # pid file.
+- make_pid_file(opts.pid_file)
+-
+- # Register a function to clean up the socket and pid file on exit.
+- atexit.register(self.quit)
+-
+- # Add signal handlers.
+- for sigint in [SIGTERM, SIGINT]:
+- signal(sigint, self.quit)
+-
+- # Load plugins into self.plugins
+- self.load_plugins(opts.plugins)
+-
+- def load_plugins(self, plugins):
+- '''Load event manager plugins.'''
+-
+- for path in plugins:
+- logger.debug('loading plugin %r', path)
+- (dir, file) = os.path.split(path)
+- name = file[:-3] if file.lower().endswith('.py') else file
+-
+- info = imp.find_module(name, [dir])
+- module = imp.load_module(name, *info)
+-
+- # Check if the plugin has a callable hook.
+- hooks = filter(callable, [getattr(module, attr, None) \
+- for attr in ['init', 'after', 'cleanup']])
+- assert hooks, "no hooks in plugin %r" % module
+-
+- logger.debug('creating plugin instance for %r plugin', name)
+- plugin = Plugin(self, name, path, module)
+- self.plugins[name] = plugin
+- logger.info('new %r', plugin)
+-
+- def create_server_socket(self):
+- '''Create the event manager daemon socket for uzbl instance duplex
+- communication.'''
+-
+- # Close old socket.
+- self.close_server_socket()
+-
+- sock = socket(AF_UNIX, SOCK_STREAM)
+- sock.bind(opts.server_socket)
+- sock.listen(5)
+-
+- self.server_socket = sock
+- logger.debug('bound server socket to %r', opts.server_socket)
+-
+- def run(self):
+- '''Main event daemon loop.'''
+-
+- logger.debug('entering main loop')
+-
+- # Create and listen on the server socket
+- self.create_server_socket()
+-
+- if opts.daemon_mode:
+- # Daemonize the process
+- daemonize()
+-
+- # Update the pid file
+- make_pid_file(opts.pid_file)
+-
+- try:
+- # Accept incoming connections and listen for incoming data
+- self.listen()
+-
+- except:
+- if not self._quit:
+- logger.critical('failed to listen', exc_info=True)
+-
+- # Clean up and exit
+- self.quit()
+-
+- logger.debug('exiting main loop')
+-
+- def listen(self):
+- '''Accept incoming connections and constantly poll instance sockets
+- for incoming data.'''
+-
+- logger.info('listening on %r', opts.server_socket)
+-
+- # Count accepted connections
+- connections = 0
+-
+- while (self.uzbls or not connections) or (not opts.auto_close):
+- socks = [self.server_socket] + self.uzbls.keys()
+- wsocks = [k for k, v in self.uzbls.items() if v.child_buffer]
+- reads, writes, errors = select(socks, wsocks, socks, 1)
+-
+- if self.server_socket in reads:
+- reads.remove(self.server_socket)
+-
+- # Accept connection and create uzbl instance.
+- child_socket = self.server_socket.accept()[0]
+- child_socket.setblocking(False)
+- self.uzbls[child_socket] = Uzbl(self, child_socket)
+- connections += 1
+-
+- for uzbl in [self.uzbls[s] for s in writes if s in self.uzbls ]:
+- uzbl.do_send()
+-
+- for uzbl in [self.uzbls[s] for s in reads if s in self.uzbls]:
+- uzbl.read()
+-
+- for uzbl in [self.uzbls[s] for s in errors if s in self.uzbls]:
+- uzbl.logger.error('socket read error')
+- uzbl.close()
+-
+- logger.info('auto closing')
+-
+- def close_server_socket(self):
+- '''Close and delete the server socket.'''
+-
+- try:
+- if self.server_socket:
+- logger.debug('closing server socket')
+- self.server_socket.close()
+- self.server_socket = None
+-
+- if os.path.exists(opts.server_socket):
+- logger.info('unlinking %r', opts.server_socket)
+- os.unlink(opts.server_socket)
+-
+- except:
+- logger.error('failed to close server socket', exc_info=True)
+-
+- def quit(self, sigint=None, *args):
+- '''Close all instance socket objects, server socket and delete the
+- pid file.'''
+-
+- if sigint == SIGTERM:
+- logger.critical('caught SIGTERM, exiting')
+-
+- elif sigint == SIGINT:
+- logger.critical('caught SIGINT, exiting')
+-
+- elif not self._quit:
+- logger.debug('shutting down event manager')
+-
+- self.close_server_socket()
+-
+- for uzbl in self.uzbls.values():
+- uzbl.close()
+-
+- del_pid_file(opts.pid_file)
+-
+- if not self._quit:
+- logger.info('event manager shut down')
+- self._quit = True
+-
+-
+-def make_pid_file(pid_file):
+- '''Creates a pid file at `pid_file`, fails silently.'''
+-
+- try:
+- logger.debug('creating pid file %r', pid_file)
+- make_dirs(pid_file)
+- pid = os.getpid()
+- fileobj = open(pid_file, 'w')
+- fileobj.write('%d' % pid)
+- fileobj.close()
+- logger.info('created pid file %r with pid %d', pid_file, pid)
+-
+- except:
+- logger.error('failed to create pid file', exc_info=True)
+-
+-
+-def del_pid_file(pid_file):
+- '''Deletes a pid file at `pid_file`, fails silently.'''
+-
+- if os.path.isfile(pid_file):
+- try:
+- logger.debug('deleting pid file %r', pid_file)
+- os.remove(pid_file)
+- logger.info('deleted pid file %r', pid_file)
+-
+- except:
+- logger.error('failed to delete pid file', exc_info=True)
+-
+-
+-def get_pid(pid_file):
+- '''Reads a pid from pid file `pid_file`, fails None.'''
+-
+- try:
+- logger.debug('reading pid file %r', pid_file)
+- fileobj = open(pid_file, 'r')
+- pid = int(fileobj.read())
+- fileobj.close()
+- logger.info('read pid %d from pid file %r', pid, pid_file)
+- return pid
+-
+- except (IOError, ValueError):
+- logger.error('failed to read pid', exc_info=True)
+- return None
+-
+-
+-def pid_running(pid):
+- '''Checks if a process with a pid `pid` is running.'''
+-
+- try:
+- os.kill(pid, 0)
+- except OSError:
+- return False
+- else:
+- return True
+-
+-
+-def term_process(pid):
+- '''Asks nicely then forces process with pid `pid` to exit.'''
+-
+- try:
+- logger.info('sending SIGTERM to process with pid %r', pid)
+- os.kill(pid, SIGTERM)
+-
+- except OSError:
+- logger.error(get_exc())
+-
+- logger.debug('waiting for process with pid %r to exit', pid)
+- start = time.time()
+- while True:
+- if not pid_running(pid):
+- logger.debug('process with pid %d exit', pid)
+- return True
+-
+- if (time.time() - start) > 5:
+- logger.warning('process with pid %d failed to exit', pid)
+- logger.info('sending SIGKILL to process with pid %d', pid)
+- try:
+- os.kill(pid, SIGKILL)
+- except:
+- logger.critical('failed to kill %d', pid, exc_info=True)
+- raise
+-
+- if (time.time() - start) > 10:
+- logger.critical('unable to kill process with pid %d', pid)
+- raise OSError
+-
+- time.sleep(0.25)
+-
+-
+-def stop_action():
+- '''Stop the event manager daemon.'''
+-
+- pid_file = opts.pid_file
+- if not os.path.isfile(pid_file):
+- logger.error('could not find running event manager with pid file %r',
+- pid_file)
+- return
+-
+- pid = get_pid(pid_file)
+- if not pid_running(pid):
+- logger.debug('no process with pid %r', pid)
+- del_pid_file(pid_file)
+- return
+-
+- logger.debug('terminating process with pid %r', pid)
+- term_process(pid)
+- del_pid_file(pid_file)
+- logger.info('stopped event manager process with pid %d', pid)
+-
+-
+-def start_action():
+- '''Start the event manager daemon.'''
+-
+- pid_file = opts.pid_file
+- if os.path.isfile(pid_file):
+- pid = get_pid(pid_file)
+- if pid_running(pid):
+- logger.error('event manager already started with pid %d', pid)
+- return
+-
+- logger.info('no process with pid %d', pid)
+- del_pid_file(pid_file)
+-
+- UzblEventDaemon().run()
+-
+-
+-def restart_action():
+- '''Restart the event manager daemon.'''
+-
+- stop_action()
+- start_action()
+-
+-
+-def list_action():
+- '''List all the plugins that would be loaded in the current search
+- dirs.'''
+-
+- names = {}
+- for plugin in opts.plugins:
+- (head, tail) = os.path.split(plugin)
+- if tail not in names:
+- names[tail] = plugin
+-
+- for plugin in sorted(names.values()):
+- print plugin
+-
+-
+-def make_parser():
+- parser = OptionParser('usage: %prog [options] {start|stop|restart|list}')
+- add = parser.add_option
+-
+- add('-v', '--verbose',
+- dest='verbose', default=2, action='count',
+- help='increase verbosity')
+-
+- add('-d', '--plugin-dir',
+- dest='plugin_dirs', action='append', metavar="DIR", default=[],
+- help='add extra plugin search dir, same as `-l "DIR/*.py"`')
+-
+- add('-l', '--load-plugin',
+- dest='load_plugins', action='append', metavar="PLUGIN", default=[],
+- help='load plugin, loads before plugins in search dirs')
+-
+- socket_location = os.path.join(CACHE_DIR, 'event_daemon')
+-
+- add('-s', '--server-socket',
+- dest='server_socket', metavar="SOCKET", default=socket_location,
+- help='server AF_UNIX socket location')
+-
+- add('-p', '--pid-file',
+- metavar="FILE", dest='pid_file',
+- help='pid file location, defaults to server socket + .pid')
+-
+- add('-n', '--no-daemon',
+- dest='daemon_mode', action='store_false', default=True,
+- help='do not daemonize the process')
+-
+- add('-a', '--auto-close',
+- dest='auto_close', action='store_true', default=False,
+- help='auto close after all instances disconnect')
+-
+- add('-i', '--no-default-dirs',
+- dest='default_dirs', action='store_false', default=True,
+- help='ignore the default plugin search dirs')
+-
+- add('-o', '--log-file',
+- dest='log_file', metavar='FILE',
+- help='write logging output to a file, defaults to server socket +'
+- ' .log')
+-
+- add('-q', '--quiet-events',
+- dest='print_events', action="store_false", default=True,
+- help="silence the printing of events to stdout")
+-
+- return parser
+-
+-
+-def init_logger():
+- log_level = logging.CRITICAL - opts.verbose * 10
+- logger = logging.getLogger()
+- logger.setLevel(max(log_level, 10))
+-
+- # Console
+- handler = logging.StreamHandler()
+- handler.setLevel(max(log_level + 10, 10))
+- handler.setFormatter(logging.Formatter(
+- '%(name)s: %(levelname)s: %(message)s'))
+- logger.addHandler(handler)
+-
+- # Logfile
+- handler = logging.FileHandler(opts.log_file, 'w', 'utf-8', 1)
+- handler.setLevel(max(log_level, 10))
+- handler.setFormatter(logging.Formatter(
+- '[%(created)f] %(name)s: %(levelname)s: %(message)s'))
+- logger.addHandler(handler)
+-
+-
+-def main():
+- global opts
+-
+- parser = make_parser()
+-
+- (opts, args) = parser.parse_args()
+-
+- opts.server_socket = expandpath(opts.server_socket)
+-
+- # Set default pid file location
+- if not opts.pid_file:
+- opts.pid_file = "%s.pid" % opts.server_socket
+-
+- else:
+- opts.pid_file = expandpath(opts.pid_file)
+-
+- # Set default log file location
+- if not opts.log_file:
+- opts.log_file = "%s.log" % opts.server_socket
+-
+- else:
+- opts.log_file = expandpath(opts.log_file)
+-
+- # Logging setup
+- init_logger()
+- logger.info('logging to %r', opts.log_file)
+-
+- plugins = {}
+-
+- # Load all `opts.load_plugins` into the plugins list
+- for path in opts.load_plugins:
+- path = expandpath(path)
+- matches = glob(path)
+- if not matches:
+- parser.error('cannot find plugin(s): %r' % path)
+-
+- for plugin in matches:
+- (head, tail) = os.path.split(plugin)
+- if tail not in plugins:
+- logger.debug('found plugin: %r', plugin)
+- plugins[tail] = plugin
+-
+- else:
+- logger.debug('ignoring plugin: %r', plugin)
+-
+- # Add default plugin locations
+- if opts.default_dirs:
+- logger.debug('adding default plugin dirs to plugin dirs list')
+- opts.plugin_dirs += [os.path.join(DATA_DIR, 'plugins/'),
+- os.path.join(PREFIX, 'share/uzbl/examples/data/plugins/')]
+-
+- else:
+- logger.debug('ignoring default plugin dirs')
+-
+- # Load all plugins in `opts.plugin_dirs` into the plugins list
+- for dir in opts.plugin_dirs:
+- dir = expandpath(dir)
+- logger.debug('searching plugin dir: %r', dir)
+- for plugin in glob(os.path.join(dir, '*.py')):
+- (head, tail) = os.path.split(plugin)
+- if tail not in plugins:
+- logger.debug('found plugin: %r', plugin)
+- plugins[tail] = plugin
+-
+- else:
+- logger.debug('ignoring plugin: %r', plugin)
+-
+- plugins = plugins.values()
+-
+- # Check all the paths in the plugins list are files
+- for plugin in plugins:
+- if not os.path.isfile(plugin):
+- parser.error('plugin not a file: %r' % plugin)
+-
+- if opts.auto_close:
+- logger.debug('will auto close')
+- else:
+- logger.debug('will not auto close')
+-
+- if opts.daemon_mode:
+- logger.debug('will daemonize')
+- else:
+- logger.debug('will not daemonize')
+-
+- opts.plugins = plugins
+-
+- # init like {start|stop|..} daemon actions
+- daemon_actions = {'start': start_action, 'stop': stop_action,
+- 'restart': restart_action, 'list': list_action}
+-
+- if len(args) == 1:
+- action = args[0]
+- if action not in daemon_actions:
+- parser.error('invalid action: %r' % action)
+-
+- elif not args:
+- action = 'start'
+- logger.warning('no daemon action given, assuming %r', action)
+-
+- else:
+- parser.error('invalid action argument: %r' % args)
+-
+- logger.info('daemon action %r', action)
+- # Do action
+- daemon_actions[action]()
+-
+- logger.debug('process CPU time: %f', time.clock())
+-
+-
+-if __name__ == "__main__":
+- main()
+-
+-
+-# vi: set et ts=4:
++#!/usr/bin/python3
++from uzbl import event_manager
++event_manager.main()
+diff --git a/bin/uzbl-tabbed b/bin/uzbl-tabbed
+index b78a54a..12fa249 100755
+--- a/bin/uzbl-tabbed
++++ b/bin/uzbl-tabbed
+@@ -47,6 +47,10 @@
+ #
+ # Simon Lipp (sloonz)
+ # Various
++#
++# Hakan Jerning
++# Wrote autosave_session patch to have a saved session even if
++# uzbl-tabbed is closed unexpectedly.
+
+
+ # Dependencies:
+@@ -85,6 +89,7 @@
+ # save_session = 1
+ # json_session = 0
+ # session_file = $HOME/.local/share/uzbl/session
++# autosave_session = 0
+ #
+ # Inherited uzbl options:
+ # icon_path = $HOME/.local/share/uzbl/uzbl.png
+@@ -209,6 +214,7 @@ config = {
+ 'save_session': True, # Save session in file when quit
+ 'saved_sessions_dir': os.path.join(DATA_DIR, 'sessions/'),
+ 'session_file': os.path.join(DATA_DIR, 'session'),
++ 'autosave_session': False, # Save session for every tab change
+
+ # Inherited uzbl options
+ 'icon_path': os.path.join(DATA_DIR, 'uzbl.png'),
+@@ -232,6 +238,11 @@ config = {
+ 'selected_https': 'foreground = "#fff"',
+ 'selected_https_text': 'foreground = "gold"',
+
++ #Explicit config file. Unlike the other configs, this one cannot be inherited
++ #from the uzbl config file, as stated above. I've only put it here because
++ #load_session() is already called in UzblTabbed.__init__.
++ 'explicit_config_file': None,
++
+ } # End of config dict.
+
+ UZBL_TABBED_VARS = config.keys()
+@@ -410,7 +421,7 @@ class GlobalEventDispatcher(EventDispatcher):
+ def new_tab(self, uri = ''):
+ self.uzbl_tabbed.new_tab(uri)
+
+- def new_tab_bg(self, uri = ''):
++ def new_bg_tab(self, uri = ''):
+ self.uzbl_tabbed.new_tab(uri, switch = False)
+
+ def new_tab_next(self, uri = ''):
+@@ -434,6 +445,15 @@ class GlobalEventDispatcher(EventDispatcher):
+ def last_tab(self):
+ self.uzbl_tabbed.goto_tab(-1)
+
++ def move_current_tab(self, index=0):
++ self.uzbl_tabbed.move_current_tab(absolute=int(index))
++
++ def move_current_tab_left(self):
++ self.uzbl_tabbed.move_current_tab(relative=-1)
++
++ def move_current_tab_right(self):
++ self.uzbl_tabbed.move_current_tab(relative=1)
++
+ def preset_tabs(self, *args):
+ self.uzbl_tabbed.run_preset_command(*args)
+
+@@ -889,6 +909,9 @@ class UzblTabbed:
+ if(uri):
+ cmd = cmd + ['--uri', str(uri)]
+
++ if config['explicit_config_file'] is not None:
++ cmd = cmd + ['-c', config['explicit_config_file']]
++
+ gobject.spawn_async(cmd, flags=gobject.SPAWN_SEARCH_PATH)
+
+ uzbl = UzblInstance(self, name, uri, title, switch)
+@@ -968,6 +991,18 @@ class UzblTabbed:
+ while tabn < 0: tabn += ntabs
+ self.goto_tab(tabn)
+
++ def move_tab(self, tab, index):
++ '''Move tab to position.'''
++ self.notebook.reorder_child(tab, index)
++ self.update_tablist()
++
++ def move_current_tab(self, absolute=None, relative=None):
++ '''Move current tab to position.'''
++ current = self.notebook.get_current_page()
++ index = absolute if absolute is not None else current + relative \
++ if current + relative < len(self.notebook) else 0
++ tab = self.notebook.get_nth_page(current)
++ self.move_tab(tab, index)
+
+ def close_tab(self, tabn=None):
+ '''Closes current tab. Supports negative indexing.'''
+@@ -1030,6 +1065,11 @@ class UzblTabbed:
+ tab = self.notebook.get_nth_page(index)
+ self.notebook.set_focus_child(tab)
+ self.update_tablist(index)
++
++ if config['save_session'] and config['autosave_session']:
++ if len(list(self.notebook)) > 1:
++ self.save_session()
++
+ return True
+
+
+@@ -1038,6 +1078,7 @@ class UzblTabbed:
+
+ for tab in self.notebook:
+ self.tabs[tab].title_changed(True)
++ self.update_tablist()
+ return True
+
+
+@@ -1261,6 +1302,8 @@ if __name__ == "__main__":
+ help="directory to create socket")
+ parser.add_option('-f', '--fifodir', dest='fifodir',
+ help="directory to create fifo")
++ parser.add_option('--config-file', dest='config_file',
++ help="configuration file for all uzbl-browser instances")
+
+ # Parse command line options
+ (options, uris) = parser.parse_args()
+@@ -1275,6 +1318,15 @@ if __name__ == "__main__":
+ import pprint
+ sys.stderr.write("%s\n" % pprint.pformat(config))
+
++ if options.config_file is not None:
++ if not os.path.exists(options.config_file):
++ errorstr = "Explicit config file {} does not exist" % (
++ options.config_file)
++ error(errorstr)
++ sys.exit(-1)
++
++ config['explicit_config_file'] = options.config_file
++
+ uzbl = UzblTabbed()
+
+ if options.socketdir:
+diff --git a/examples/config/config b/examples/config/config
+index 11f1d82..d607cb4 100644
+--- a/examples/config/config
++++ b/examples/config/config
+@@ -8,6 +8,7 @@ set prefix = @(echo $PREFIX)@
+ set data_home = @(echo $XDG_DATA_HOME)@
+ set cache_home = @(echo $XDG_CACHE_HOME)@
+ set config_home = @(echo $XDG_CONFIG_HOME)@
++set local_storage_path = @data_home/uzbl/databases/
+
+ # Interface paths.
+ set fifo_dir = /tmp
+@@ -70,7 +71,6 @@ set download_handler = sync_spawn @scripts_dir/download.sh
+ @on_event LOAD_COMMIT @set_status <span foreground="green">recv</span>
+
+ # add some javascript to the page for other 'js' and 'script' commands to access later.
+- at on_event LOAD_COMMIT js uzbl = {};
+ @on_event LOAD_COMMIT script @scripts_dir/formfiller.js
+ @on_event LOAD_COMMIT script @scripts_dir/follow.js
+
+@@ -86,6 +86,8 @@ set download_handler = sync_spawn @scripts_dir/download.sh
+ # Switch to command mode if anything else is clicked
+ @on_event ROOT_ACTIVE @set_mode command
+
++ at on_event AUTHENTICATE spawn @scripts_dir/auth.py "%1" "%2" "%3"
++
+ # Example CONFIG_CHANGED event handler
+ #@on_event CONFIG_CHANGED print Config changed: %1 = %2
+
+@@ -97,6 +99,10 @@ set download_handler = sync_spawn @scripts_dir/download.sh
+ # Custom CSS can be defined here, including link follower hint styles
+ set stylesheet_uri = file://@config_home/uzbl/style.css
+
++# If WebKits builtin authentication dialog should be used, if enabling remember
++# to disable external authentication handlers
++set enable_builtin_auth = 0
++
+ set show_status = 1
+ set status_top = 0
+ set status_background = #303030
+@@ -138,6 +144,8 @@ set useragent = Uzbl (Webkit @{WEBKIT_MAJOR}.@{WEBKIT_MINOR}) (@(+uname
+
+ # === Configure cookie blacklist ========================================================
+
++set cookie_policy = 0
++
+ # Accept 'session cookies' from uzbl.org (when you have a whitelist all other cookies are dropped)
+ #request WHITELIST_COOKIE domain 'uzbl.org$' expires '^$'
+
+@@ -404,6 +412,9 @@ set formfiller = spawn @scripts_dir/formfiller.sh
+ @cbind gt = event NEXT_TAB
+ @cbind gT = event PREV_TAB
+ @cbind gi<index:>_ = event GOTO_TAB %s
++ at cbind <Ctrl><Left> = event MOVE_CURRENT_TAB_LEFT
++ at cbind <Ctrl><Right> = event MOVE_CURRENT_TAB_RIGHT
++ at cbind gm<index:>_ = event MOVE_CURRENT_TAB %s
+
+ # Preset loading
+ set preset = event PRESET_TABS
+diff --git a/examples/data/plugins/bind.py b/examples/data/plugins/bind.py
+deleted file mode 100644
+index fc8b392..0000000
+--- a/examples/data/plugins/bind.py
++++ /dev/null
+@@ -1,462 +0,0 @@
+-'''Plugin provides support for binds in uzbl.
+-
+-For example:
+- event BIND ZZ = exit -> bind('ZZ', 'exit')
+- event BIND o _ = uri %s -> bind('o _', 'uri %s')
+- event BIND fl* = sh 'echo %s' -> bind('fl*', "sh 'echo %s'")
+-
+-And it is also possible to execute a function on activation:
+- bind('DD', myhandler)
+-'''
+-
+-import sys
+-import re
+-
+-# Commonly used regular expressions.
+-MOD_START = re.compile('^<([A-Z][A-Za-z0-9-_]*)>').match
+-# Matches <x:y>, <'x':y>, <:'y'>, <x!y>, <'x'!y>, ...
+-PROMPTS = '<(\"[^\"]*\"|\'[^\']*\'|[^:!>]*)(:|!)(\"[^\"]*\"|\'[^\']*\'|[^>]*)>'
+-FIND_PROMPTS = re.compile(PROMPTS).split
+-VALID_MODE = re.compile('^(-|)[A-Za-z0-9][A-Za-z0-9_]*$').match
+-
+-# For accessing a bind glob stack.
+-ON_EXEC, HAS_ARGS, MOD_CMD, GLOB, MORE = range(5)
+-
+-
+-# Custom errors.
+-class ArgumentError(Exception): pass
+-
+-
+-class Bindlet(object):
+- '''Per-instance bind status/state tracker.'''
+-
+- def __init__(self, uzbl):
+- self.binds = {'global': {}}
+- self.uzbl = uzbl
+- self.depth = 0
+- self.args = []
+- self.last_mode = None
+- self.after_cmds = None
+- self.stack_binds = []
+-
+- # A subset of the global mode binds containing non-stack and modkey
+- # activiated binds for use in the stack mode.
+- self.globals = []
+-
+-
+- def __getitem__(self, key):
+- return self.get_binds(key)
+-
+-
+- def reset(self):
+- '''Reset the tracker state and return to last mode.'''
+-
+- self.depth = 0
+- self.args = []
+- self.after_cmds = None
+- self.stack_binds = []
+-
+- if self.last_mode:
+- mode, self.last_mode = self.last_mode, None
+- self.uzbl.config['mode'] = mode
+-
+- del self.uzbl.config['keycmd_prompt']
+-
+-
+- def stack(self, bind, args, depth):
+- '''Enter or add new bind in the next stack level.'''
+-
+- if self.depth != depth:
+- if bind not in self.stack_binds:
+- self.stack_binds.append(bind)
+-
+- return
+-
+- mode = self.uzbl.config.get('mode', None)
+- if mode != 'stack':
+- self.last_mode = mode
+- self.uzbl.config['mode'] = 'stack'
+-
+- self.stack_binds = [bind,]
+- self.args += args
+- self.depth += 1
+- self.after_cmds = bind.prompts[depth]
+-
+-
+- def after(self):
+- '''If a stack was triggered then set the prompt and default value.'''
+-
+- if self.after_cmds is None:
+- return
+-
+- (prompt, is_cmd, set), self.after_cmds = self.after_cmds, None
+-
+- self.uzbl.clear_keycmd()
+- if prompt:
+- self.uzbl.config['keycmd_prompt'] = prompt
+-
+- if set and is_cmd:
+- self.uzbl.send(set)
+-
+- elif set and not is_cmd:
+- self.uzbl.send('event SET_KEYCMD %s' % set)
+-
+-
+- def get_binds(self, mode=None):
+- '''Return the mode binds + globals. If we are stacked then return
+- the filtered stack list and modkey & non-stack globals.'''
+-
+- if mode is None:
+- mode = self.uzbl.config.get('mode', None)
+-
+- if not mode:
+- mode = 'global'
+-
+- if self.depth:
+- return self.stack_binds + self.globals
+-
+- globals = self.binds['global']
+- if mode not in self.binds or mode == 'global':
+- return filter(None, globals.values())
+-
+- binds = dict(globals.items() + self.binds[mode].items())
+- return filter(None, binds.values())
+-
+-
+- def add_bind(self, mode, glob, bind=None):
+- '''Insert (or override) a bind into the mode bind dict.'''
+-
+- if mode not in self.binds:
+- self.binds[mode] = {glob: bind}
+- return
+-
+- binds = self.binds[mode]
+- binds[glob] = bind
+-
+- if mode == 'global':
+- # Regen the global-globals list.
+- self.globals = []
+- for bind in binds.values():
+- if bind is not None and bind.is_global:
+- self.globals.append(bind)
+-
+-
+-def ismodbind(glob):
+- '''Return True if the glob specifies a modbind.'''
+-
+- return bool(MOD_START(glob))
+-
+-
+-def split_glob(glob):
+- '''Take a string of the form "<Mod1><Mod2>cmd _" and return a list of the
+- modkeys in the glob and the command.'''
+-
+- mods = set()
+- while True:
+- match = MOD_START(glob)
+- if not match:
+- break
+-
+- end = match.span()[1]
+- mods.add(glob[:end])
+- glob = glob[end:]
+-
+- return (mods, glob)
+-
+-
+-class Bind(object):
+-
+- # Class attribute to hold the number of Bind classes created.
+- counter = [0,]
+-
+- def __init__(self, glob, handler, *args, **kargs):
+- self.is_callable = callable(handler)
+- self._repr_cache = None
+-
+- if not glob:
+- raise ArgumentError('glob cannot be blank')
+-
+- if self.is_callable:
+- self.function = handler
+- self.args = args
+- self.kargs = kargs
+-
+- elif kargs:
+- raise ArgumentError('cannot supply kargs for uzbl commands')
+-
+- elif hasattr(handler, '__iter__'):
+- self.commands = handler
+-
+- else:
+- self.commands = [handler,] + list(args)
+-
+- self.glob = glob
+-
+- # Assign unique id.
+- self.counter[0] += 1
+- self.bid = self.counter[0]
+-
+- self.split = split = FIND_PROMPTS(glob)
+- self.prompts = []
+- for (prompt, cmd, set) in zip(split[1::4], split[2::4], split[3::4]):
+- prompt, set = map(unquote, [prompt, set])
+- cmd = True if cmd == '!' else False
+- if prompt and prompt[-1] != ":":
+- prompt = "%s:" % prompt
+-
+- self.prompts.append((prompt, cmd, set))
+-
+- # Check that there is nothing like: fl*<int:>*
+- for glob in split[:-1:4]:
+- if glob.endswith('*'):
+- msg = "token '*' not at the end of a prompt bind: %r" % split
+- raise SyntaxError(msg)
+-
+- # Check that there is nothing like: fl<prompt1:><prompt2:>_
+- for glob in split[4::4]:
+- if not glob:
+- msg = 'found null segment after first prompt: %r' % split
+- raise SyntaxError(msg)
+-
+- stack = []
+- for (index, glob) in enumerate(reversed(split[::4])):
+- # Is the binding a MODCMD or KEYCMD:
+- mod_cmd = ismodbind(glob)
+-
+- # Do we execute on UPDATES or EXEC events?
+- on_exec = True if glob[-1] in ['!', '_'] else False
+-
+- # Does the command take arguments?
+- has_args = True if glob[-1] in ['*', '_'] else False
+-
+- glob = glob[:-1] if has_args or on_exec else glob
+- mods, glob = split_glob(glob)
+- stack.append((on_exec, has_args, mods, glob, index))
+-
+- self.stack = list(reversed(stack))
+- self.is_global = (len(self.stack) == 1 and self.stack[0][MOD_CMD])
+-
+-
+- def __getitem__(self, depth):
+- '''Get bind info at a depth.'''
+-
+- if self.is_global:
+- return self.stack[0]
+-
+- return self.stack[depth]
+-
+-
+- def __repr__(self):
+- if self._repr_cache:
+- return self._repr_cache
+-
+- args = ['glob=%r' % self.glob, 'bid=%d' % self.bid]
+-
+- if self.is_callable:
+- args.append('function=%r' % self.function)
+- if self.args:
+- args.append('args=%r' % self.args)
+-
+- if self.kargs:
+- args.append('kargs=%r' % self.kargs)
+-
+- else:
+- cmdlen = len(self.commands)
+- cmds = self.commands[0] if cmdlen == 1 else self.commands
+- args.append('command%s=%r' % ('s' if cmdlen-1 else '', cmds))
+-
+- self._repr_cache = '<Bind(%s)>' % ', '.join(args)
+- return self._repr_cache
+-
+-
+-def exec_bind(uzbl, bind, *args, **kargs):
+- '''Execute bind objects.'''
+-
+- uzbl.event("EXEC_BIND", bind, args, kargs)
+-
+- if bind.is_callable:
+- args += bind.args
+- kargs = dict(bind.kargs.items()+kargs.items())
+- bind.function(uzbl, *args, **kargs)
+- return
+-
+- if kargs:
+- raise ArgumentError('cannot supply kargs for uzbl commands')
+-
+- commands = []
+- cmd_expand = uzbl.cmd_expand
+- for cmd in bind.commands:
+- cmd = cmd_expand(cmd, args)
+- uzbl.send(cmd)
+-
+-
+-def mode_bind(uzbl, modes, glob, handler=None, *args, **kargs):
+- '''Add a mode bind.'''
+-
+- bindlet = uzbl.bindlet
+-
+- if not hasattr(modes, '__iter__'):
+- modes = unicode(modes).split(',')
+-
+- # Sort and filter binds.
+- modes = filter(None, map(unicode.strip, modes))
+-
+- if callable(handler) or (handler is not None and handler.strip()):
+- bind = Bind(glob, handler, *args, **kargs)
+-
+- else:
+- bind = None
+-
+- for mode in modes:
+- if not VALID_MODE(mode):
+- raise NameError('invalid mode name: %r' % mode)
+-
+- for mode in modes:
+- if mode[0] == '-':
+- mode, bind = mode[1:], None
+-
+- bindlet.add_bind(mode, glob, bind)
+- uzbl.event('ADDED_MODE_BIND', mode, glob, bind)
+-
+-
+-def bind(uzbl, glob, handler, *args, **kargs):
+- '''Legacy bind function.'''
+-
+- mode_bind(uzbl, 'global', glob, handler, *args, **kargs)
+-
+-
+-def parse_mode_bind(uzbl, args):
+- '''Parser for the MODE_BIND event.
+-
+- Example events:
+- MODE_BIND <mode> <bind> = <command>
+- MODE_BIND command o<location:>_ = uri %s
+- MODE_BIND insert,command <BackSpace> = ...
+- MODE_BIND global ... = ...
+- MODE_BIND global,-insert ... = ...
+- '''
+-
+- if not args:
+- raise ArgumentError('missing bind arguments')
+-
+- split = map(unicode.strip, args.split(' ', 1))
+- if len(split) != 2:
+- raise ArgumentError('missing mode or bind section: %r' % args)
+-
+- modes, args = split[0].split(','), split[1]
+- split = map(unicode.strip, args.split('=', 1))
+- if len(split) != 2:
+- raise ArgumentError('missing delimiter in bind section: %r' % args)
+-
+- glob, command = split
+- mode_bind(uzbl, modes, glob, command)
+-
+-
+-def parse_bind(uzbl, args):
+- '''Legacy parsing of the BIND event and conversion to the new format.
+-
+- Example events:
+- request BIND <bind> = <command>
+- request BIND o<location:>_ = uri %s
+- request BIND <BackSpace> = ...
+- request BIND ... = ...
+- '''
+-
+- parse_mode_bind(uzbl, "global %s" % args)
+-
+-
+-def mode_changed(uzbl, mode):
+- '''Clear the stack on all non-stack mode changes.'''
+-
+- if mode != 'stack':
+- uzbl.bindlet.reset()
+-
+-
+-def match_and_exec(uzbl, bind, depth, modstate, keylet, bindlet):
+- (on_exec, has_args, mod_cmd, glob, more) = bind[depth]
+- cmd = keylet.modcmd if mod_cmd else keylet.keycmd
+-
+- if mod_cmd and modstate != mod_cmd:
+- return False
+-
+- if has_args:
+- if not cmd.startswith(glob):
+- return False
+-
+- args = [cmd[len(glob):],]
+-
+- elif cmd != glob:
+- return False
+-
+- else:
+- args = []
+-
+- if bind.is_global or (not more and depth == 0):
+- exec_bind(uzbl, bind, *args)
+- if not has_args:
+- uzbl.clear_current()
+-
+- return True
+-
+- elif more:
+- bindlet.stack(bind, args, depth)
+- (on_exec, has_args, mod_cmd, glob, more) = bind[depth+1]
+- if not on_exec and has_args and not glob and not more:
+- exec_bind(uzbl, bind, *(args+['',]))
+-
+- return False
+-
+- args = bindlet.args + args
+- exec_bind(uzbl, bind, *args)
+- if not has_args or on_exec:
+- del uzbl.config['mode']
+- bindlet.reset()
+-
+- return True
+-
+-
+-def key_event(uzbl, modstate, keylet, mod_cmd=False, on_exec=False):
+- bindlet = uzbl.bindlet
+- depth = bindlet.depth
+- for bind in bindlet.get_binds():
+- t = bind[depth]
+- if (bool(t[MOD_CMD]) != mod_cmd) or (t[ON_EXEC] != on_exec):
+- continue
+-
+- if match_and_exec(uzbl, bind, depth, modstate, keylet, bindlet):
+- return
+-
+- bindlet.after()
+-
+- # Return to the previous mode if the KEYCMD_EXEC keycmd doesn't match any
+- # binds in the stack mode.
+- if on_exec and not mod_cmd and depth and depth == bindlet.depth:
+- del uzbl.config['mode']
+-
+-
+-# plugin init hook
+-def init(uzbl):
+- '''Export functions and connect handlers to events.'''
+-
+- connect_dict(uzbl, {
+- 'BIND': parse_bind,
+- 'MODE_BIND': parse_mode_bind,
+- 'MODE_CHANGED': mode_changed,
+- })
+-
+- # Connect key related events to the key_event function.
+- events = [['KEYCMD_UPDATE', 'KEYCMD_EXEC'],
+- ['MODCMD_UPDATE', 'MODCMD_EXEC']]
+-
+- for mod_cmd in range(2):
+- for on_exec in range(2):
+- event = events[mod_cmd][on_exec]
+- connect(uzbl, event, key_event, bool(mod_cmd), bool(on_exec))
+-
+- export_dict(uzbl, {
+- 'bind': bind,
+- 'mode_bind': mode_bind,
+- 'bindlet': Bindlet(uzbl),
+- })
+-
+-# vi: set et ts=4:
+diff --git a/examples/data/plugins/cmd_expand.py b/examples/data/plugins/cmd_expand.py
+deleted file mode 100644
+index b007975..0000000
+--- a/examples/data/plugins/cmd_expand.py
++++ /dev/null
+@@ -1,40 +0,0 @@
+-def escape(str):
+- for (level, char) in [(3, '\\'), (2, "'"), (2, '"'), (1, '@')]:
+- str = str.replace(char, (level * '\\') + char)
+-
+- return str
+-
+-
+-def cmd_expand(uzbl, cmd, args):
+- '''Exports a function that provides the following
+- expansions in any uzbl command string:
+-
+- %s = replace('%s', ' '.join(args))
+- %r = replace('%r', "'%s'" % escaped(' '.join(args)))
+- %1 = replace('%1', arg[0])
+- %2 = replace('%2', arg[1])
+- %n = replace('%n', arg[n-1])
+- '''
+-
+- # Ensure (1) all string representable and (2) correct string encoding.
+- args = map(unicode, args)
+-
+- # Direct string replace.
+- if '%s' in cmd:
+- cmd = cmd.replace('%s', ' '.join(args))
+-
+- # Escaped and quoted string replace.
+- if '%r' in cmd:
+- cmd = cmd.replace('%r', "'%s'" % escape(' '.join(args)))
+-
+- # Arg index string replace.
+- for (index, arg) in enumerate(args):
+- index += 1
+- if '%%%d' % index in cmd:
+- cmd = cmd.replace('%%%d' % index, unicode(arg))
+-
+- return cmd
+-
+-# plugin init hook
+-def init(uzbl):
+- export(uzbl, 'cmd_expand', cmd_expand)
+diff --git a/examples/data/plugins/completion.py b/examples/data/plugins/completion.py
+deleted file mode 100644
+index e8c7f34..0000000
+--- a/examples/data/plugins/completion.py
++++ /dev/null
+@@ -1,179 +0,0 @@
+-'''Keycmd completion.'''
+-
+-import re
+-
+-# Completion level
+-NONE, ONCE, LIST, COMPLETE = range(4)
+-
+-# The reverse keyword finding re.
+-FIND_SEGMENT = re.compile("(\@[\w_]+|set[\s]+[\w_]+|[\w_]+)$").findall
+-
+-# Formats
+-LIST_FORMAT = "<span> %s </span>"
+-ITEM_FORMAT = "<span @hint_style>%s</span>%s"
+-
+-def escape(str):
+- return str.replace("@", "\@")
+-
+-
+-def get_incomplete_keyword(uzbl):
+- '''Gets the segment of the keycmd leading up to the cursor position and
+- uses a regular expression to search backwards finding parially completed
+- keywords or @variables. Returns a null string if the correct completion
+- conditions aren't met.'''
+-
+- keylet = uzbl.keylet
+- left_segment = keylet.keycmd[:keylet.cursor]
+- partial = (FIND_SEGMENT(left_segment) + ['',])[0].lstrip()
+- if partial.startswith('set '):
+- return ('@%s' % partial[4:].lstrip(), True)
+-
+- return (partial, False)
+-
+-
+-def stop_completion(uzbl, *args):
+- '''Stop command completion and return the level to NONE.'''
+-
+- uzbl.completion.level = NONE
+- del uzbl.config['completion_list']
+-
+-
+-def complete_completion(uzbl, partial, hint, set_completion=False):
+- '''Inject the remaining porition of the keyword into the keycmd then stop
+- the completioning.'''
+-
+- if set_completion:
+- remainder = "%s = " % hint[len(partial):]
+-
+- else:
+- remainder = "%s " % hint[len(partial):]
+-
+- uzbl.inject_keycmd(remainder)
+- stop_completion(uzbl)
+-
+-
+-def partial_completion(uzbl, partial, hint):
+- '''Inject a common portion of the hints into the keycmd.'''
+-
+- remainder = hint[len(partial):]
+- uzbl.inject_keycmd(remainder)
+-
+-
+-def update_completion_list(uzbl, *args):
+- '''Checks if the user still has a partially completed keyword under his
+- cursor then update the completion hints list.'''
+-
+- partial = get_incomplete_keyword(uzbl)[0]
+- if not partial:
+- return stop_completion(uzbl)
+-
+- if uzbl.completion.level < LIST:
+- return
+-
+- hints = filter(lambda h: h.startswith(partial), uzbl.completion)
+- if not hints:
+- del uzbl.config['completion_list']
+- return
+-
+- j = len(partial)
+- l = [ITEM_FORMAT % (escape(h[:j]), h[j:]) for h in sorted(hints)]
+- uzbl.config['completion_list'] = LIST_FORMAT % ' '.join(l)
+-
+-
+-def start_completion(uzbl, *args):
+-
+- comp = uzbl.completion
+- if comp.locked:
+- return
+-
+- (partial, set_completion) = get_incomplete_keyword(uzbl)
+- if not partial:
+- return stop_completion(uzbl)
+-
+- if comp.level < COMPLETE:
+- comp.level += 1
+-
+- hints = filter(lambda h: h.startswith(partial), comp)
+- if not hints:
+- return
+-
+- elif len(hints) == 1:
+- comp.lock()
+- complete_completion(uzbl, partial, hints[0], set_completion)
+- comp.unlock()
+- return
+-
+- elif partial in hints and comp.level == COMPLETE:
+- comp.lock()
+- complete_completion(uzbl, partial, partial, set_completion)
+- comp.unlock()
+- return
+-
+- smalllen, smallest = sorted([(len(h), h) for h in hints])[0]
+- common = ''
+- for i in range(len(partial), smalllen):
+- char, same = smallest[i], True
+- for hint in hints:
+- if hint[i] != char:
+- same = False
+- break
+-
+- if not same:
+- break
+-
+- common += char
+-
+- if common:
+- comp.lock()
+- partial_completion(uzbl, partial, partial+common)
+- comp.unlock()
+-
+- update_completion_list(uzbl)
+-
+-
+-def add_builtins(uzbl, builtins):
+- '''Pump the space delimited list of builtin commands into the
+- builtin list.'''
+-
+- uzbl.completion.update(builtins.split())
+-
+-
+-def add_config_key(uzbl, key, value):
+- '''Listen on the CONFIG_CHANGED event and add config keys to the variable
+- list for @var<Tab> like expansion support.'''
+-
+- uzbl.completion.add("@%s" % key)
+-
+-
+-class Completions(set):
+- def __init__(self):
+- set.__init__(self)
+- self.locked = False
+- self.level = NONE
+-
+- def lock(self):
+- self.locked = True
+-
+- def unlock(self):
+- self.locked = False
+-
+-
+-def init(uzbl):
+- '''Export functions and connect handlers to events.'''
+-
+- export_dict(uzbl, {
+- 'completion': Completions(),
+- 'start_completion': start_completion,
+- })
+-
+- connect_dict(uzbl, {
+- 'BUILTINS': add_builtins,
+- 'CONFIG_CHANGED': add_config_key,
+- 'KEYCMD_CLEARED': stop_completion,
+- 'KEYCMD_EXEC': stop_completion,
+- 'KEYCMD_UPDATE': update_completion_list,
+- 'START_COMPLETION': start_completion,
+- 'STOP_COMPLETION': stop_completion,
+- })
+-
+- uzbl.send('dump_config_as_events')
+diff --git a/examples/data/plugins/config.py b/examples/data/plugins/config.py
+deleted file mode 100644
+index c9bdf67..0000000
+--- a/examples/data/plugins/config.py
++++ /dev/null
+@@ -1,91 +0,0 @@
+-from re import compile
+-from types import BooleanType
+-from UserDict import DictMixin
+-
+-valid_key = compile('^[A-Za-z0-9_\.]+$').match
+-
+-class Config(DictMixin):
+- def __init__(self, uzbl):
+- self.uzbl = uzbl
+-
+- # Create the base dict and map allowed methods to `self`.
+- self.data = data = {}
+-
+- methods = ['__contains__', '__getitem__', '__iter__',
+- '__len__', 'get', 'has_key', 'items', 'iteritems',
+- 'iterkeys', 'itervalues', 'values']
+-
+- for method in methods:
+- setattr(self, method, getattr(data, method))
+-
+-
+- def __setitem__(self, key, value):
+- self.set(key, value)
+-
+- def __delitem__(self, key):
+- self.set(key)
+-
+- def update(self, other=None, **kwargs):
+- if other is None:
+- other = {}
+-
+- for (key, value) in dict(other).items() + kwargs.items():
+- self[key] = value
+-
+-
+- def set(self, key, value='', force=False):
+- '''Generates a `set <key> = <value>` command string to send to the
+- current uzbl instance.
+-
+- Note that the config dict isn't updated by this function. The config
+- dict is only updated after a successful `VARIABLE_SET ..` event
+- returns from the uzbl instance.'''
+-
+- assert valid_key(key)
+-
+- if type(value) == BooleanType:
+- value = int(value)
+-
+- else:
+- value = unicode(value)
+- assert '\n' not in value
+-
+- if not force and key in self and self[key] == value:
+- return
+-
+- self.uzbl.send(u'set %s = %s' % (key, value))
+-
+-
+-def parse_set_event(uzbl, args):
+- '''Parse `VARIABLE_SET <var> <type> <value>` event and load the
+- (key, value) pair into the `uzbl.config` dict.'''
+-
+- (key, type, raw_value) = (args.split(' ', 2) + ['',])[:3]
+-
+- assert valid_key(key)
+- assert type in types
+-
+- new_value = types[type](raw_value)
+- old_value = uzbl.config.get(key, None)
+-
+- # Update new value.
+- uzbl.config.data[key] = new_value
+-
+- if old_value != new_value:
+- uzbl.event('CONFIG_CHANGED', key, new_value)
+-
+- # Cleanup null config values.
+- if type == 'str' and not new_value:
+- del uzbl.config.data[key]
+-
+-
+-# plugin init hook
+-def init(uzbl):
+- global types
+- types = {'int': int, 'float': float, 'str': unquote}
+- export(uzbl, 'config', Config(uzbl))
+- connect(uzbl, 'VARIABLE_SET', parse_set_event)
+-
+-# plugin cleanup hook
+-def cleanup(uzbl):
+- uzbl.config.data.clear()
+diff --git a/examples/data/plugins/cookies.py b/examples/data/plugins/cookies.py
+deleted file mode 100644
+index bf59e96..0000000
+--- a/examples/data/plugins/cookies.py
++++ /dev/null
+@@ -1,222 +0,0 @@
+-""" Basic cookie manager
+- forwards cookies to all other instances connected to the event manager"""
+-
+-from collections import defaultdict
+-import os, re, stat
+-
+-# these are symbolic names for the components of the cookie tuple
+-symbolic = {'domain': 0, 'path':1, 'name':2, 'value':3, 'scheme':4, 'expires':5}
+-
+-# allows for partial cookies
+-# ? allow wildcard in key
+-def match(key, cookie):
+- for k,c in zip(key,cookie):
+- if k != c:
+- return False
+- return True
+-
+-class NullStore(object):
+- def add_cookie(self, rawcookie, cookie):
+- pass
+-
+- def delete_cookie(self, rkey, key):
+- pass
+-
+-class ListStore(list):
+- def add_cookie(self, rawcookie, cookie):
+- self.append(rawcookie)
+-
+- def delete_cookie(self, rkey, key):
+- self[:] = [x for x in self if not match(key, splitquoted(x))]
+-
+-class TextStore(object):
+- def __init__(self, filename):
+- self.filename = filename
+- try:
+- # make sure existing cookie jar is not world-open
+- perm_mode = os.stat(self.filename).st_mode
+- if (perm_mode & (stat.S_IRWXO | stat.S_IRWXG)) > 0:
+- safe_perm = stat.S_IMODE(perm_mode) & ~(stat.S_IRWXO | stat.S_IRWXG)
+- os.chmod(self.filename, safe_perm)
+- except OSError:
+- pass
+-
+- def as_event(self, cookie):
+- """Convert cookie.txt row to uzbls cookie event format"""
+- scheme = {
+- 'TRUE' : 'https',
+- 'FALSE' : 'http'
+- }
+- extra = ''
+- if cookie[0].startswith("#HttpOnly_"):
+- extra = 'Only'
+- domain = cookie[0][len("#HttpOnly_"):]
+- elif cookie[0].startswith('#'):
+- return None
+- else:
+- domain = cookie[0]
+- try:
+- return (domain,
+- cookie[2],
+- cookie[5],
+- cookie[6],
+- scheme[cookie[3]] + extra,
+- cookie[4])
+- except (KeyError,IndexError):
+- # Let malformed rows pass through like comments
+- return None
+-
+- def as_file(self, cookie):
+- """Convert cookie event to cookie.txt row"""
+- secure = {
+- 'https' : 'TRUE',
+- 'http' : 'FALSE',
+- 'httpsOnly' : 'TRUE',
+- 'httpOnly' : 'FALSE'
+- }
+- http_only = {
+- 'https' : '',
+- 'http' : '',
+- 'httpsOnly' : '#HttpOnly_',
+- 'httpOnly' : '#HttpOnly_'
+- }
+- return (http_only[cookie[4]] + cookie[0],
+- 'TRUE' if cookie[0].startswith('.') else 'FALSE',
+- cookie[1],
+- secure[cookie[4]],
+- cookie[5],
+- cookie[2],
+- cookie[3])
+-
+- def add_cookie(self, rawcookie, cookie):
+- assert len(cookie) == 6
+-
+- # delete equal cookies (ignoring expire time, value and secure flag)
+- self.delete_cookie(None, cookie[:-3])
+-
+- # restrict umask before creating the cookie jar
+- curmask=os.umask(0)
+- os.umask(curmask| stat.S_IRWXO | stat.S_IRWXG)
+-
+- first = not os.path.exists(self.filename)
+- with open(self.filename, 'a') as f:
+- if first:
+- print >> f, "# HTTP Cookie File"
+- print >> f, '\t'.join(self.as_file(cookie))
+- os.umask(curmask)
+-
+- def delete_cookie(self, rkey, key):
+- if not os.path.exists(self.filename):
+- return
+-
+- # restrict umask before creating the cookie jar
+- curmask=os.umask(0)
+- os.umask(curmask | stat.S_IRWXO | stat.S_IRWXG)
+-
+- # read all cookies
+- with open(self.filename, 'r') as f:
+- cookies = f.readlines()
+-
+- # write those that don't match the cookie to delete
+- with open(self.filename, 'w') as f:
+- for l in cookies:
+- c = self.as_event(l.split('\t'))
+- if c is None or not match(key, c):
+- print >> f, l,
+- os.umask(curmask)
+-
+-xdg_data_home = os.environ.get('XDG_DATA_HOME', os.path.join(os.environ['HOME'], '.local/share'))
+-DefaultStore = TextStore(os.path.join(xdg_data_home, 'uzbl/cookies.txt'))
+-SessionStore = TextStore(os.path.join(xdg_data_home, 'uzbl/session-cookies.txt'))
+-
+-def match_list(_list, cookie):
+- for matcher in _list:
+- for component, match in matcher:
+- if match(cookie[component]) is None:
+- break
+- else:
+- return True
+- return False
+-
+-# accept a cookie only when:
+-# a. there is no whitelist and the cookie is in the blacklist
+-# b. the cookie is in the whitelist and not in the blacklist
+-def accept_cookie(uzbl, cookie):
+- if uzbl.cookie_whitelist:
+- if match_list(uzbl.cookie_whitelist, cookie):
+- return not match_list(uzbl.cookie_blacklist, cookie)
+- return False
+-
+- return not match_list(uzbl.cookie_blacklist, cookie)
+-
+-def expires_with_session(uzbl, cookie):
+- return cookie[5] == ''
+-
+-def get_recipents(uzbl):
+- """ get a list of Uzbl instances to send the cookie too. """
+- # This could be a lot more interesting
+- return [u for u in uzbl.parent.uzbls.values() if u is not uzbl]
+-
+-def get_store(uzbl, session=False):
+- if session:
+- return SessionStore
+- return DefaultStore
+-
+-def add_cookie(uzbl, cookie):
+- splitted = splitquoted(cookie)
+- if accept_cookie(uzbl, splitted):
+- for u in get_recipents(uzbl):
+- u.send('add_cookie %s' % cookie)
+-
+- get_store(uzbl, expires_with_session(uzbl, splitted)).add_cookie(cookie, splitted)
+- else:
+- logger.debug('cookie %r is blacklisted' % splitted)
+- uzbl.send('delete_cookie %s' % cookie)
+-
+-def delete_cookie(uzbl, cookie):
+- for u in get_recipents(uzbl):
+- u.send('delete_cookie %s' % cookie)
+-
+- splitted = splitquoted(cookie)
+- if len(splitted) == 6:
+- get_store(uzbl, expires_with_session(uzbl, splitted)).delete_cookie(cookie, splitted)
+- else:
+- for store in set([get_store(uzbl, session) for session in (True, False)]):
+- store.delete_cookie(cookie, splitted)
+-
+-# add a cookie matcher to a whitelist or a blacklist.
+-# a matcher is a list of (component, re) tuples that matches a cookie when the
+-# "component" part of the cookie matches the regular expression "re".
+-# "component" is one of the keys defined in the variable "symbolic" above,
+-# or the index of a component of a cookie tuple.
+-def add_cookie_matcher(_list, arg):
+- args = splitquoted(arg)
+- mlist = []
+- for (component, regexp) in zip(args[0::2], args[1::2]):
+- try:
+- component = symbolic[component]
+- except KeyError:
+- component = int(component)
+- assert component <= 5
+- mlist.append((component, re.compile(regexp).search))
+- _list.append(mlist)
+-
+-def blacklist(uzbl, arg):
+- add_cookie_matcher(uzbl.cookie_blacklist, arg)
+-
+-def whitelist(uzbl, arg):
+- add_cookie_matcher(uzbl.cookie_whitelist, arg)
+-
+-def init(uzbl):
+- connect_dict(uzbl, {
+- 'ADD_COOKIE': add_cookie,
+- 'DELETE_COOKIE': delete_cookie,
+- 'BLACKLIST_COOKIE': blacklist,
+- 'WHITELIST_COOKIE': whitelist
+- })
+- export_dict(uzbl, {
+- 'cookie_blacklist' : [],
+- 'cookie_whitelist' : []
+- })
+-
+-# vi: set et ts=4:
+diff --git a/examples/data/plugins/downloads.py b/examples/data/plugins/downloads.py
+deleted file mode 100644
+index 8d796ce..0000000
+--- a/examples/data/plugins/downloads.py
++++ /dev/null
+@@ -1,77 +0,0 @@
+-# this plugin does a very simple display of download progress. to use it, add
+-# @downloads to your status_format.
+-
+-import os
+-ACTIVE_DOWNLOADS = {}
+-
+-# after a download's status has changed this is called to update the status bar
+-def update_download_section(uzbl):
+- global ACTIVE_DOWNLOADS
+-
+- if len(ACTIVE_DOWNLOADS):
+- # add a newline before we list downloads
+- result = '
downloads:'
+- for path in ACTIVE_DOWNLOADS:
+- # add each download
+- fn = os.path.basename(path)
+- progress, = ACTIVE_DOWNLOADS[path]
+-
+- dl = " %s (%d%%)" % (fn, progress * 100)
+-
+- # replace entities to make sure we don't break our markup
+- # (this could be done with an @[]@ expansion in uzbl, but then we
+- # can't use the
above to make a new line)
+- dl = dl.replace("&", "&").replace("<", "<")
+- result += dl
+- else:
+- result = ''
+-
+- # and the result gets saved to an uzbl variable that can be used in
+- # status_format
+- if uzbl.config.get('downloads', '') != result:
+- uzbl.config['downloads'] = result
+-
+-def download_started(uzbl, args):
+- # parse the arguments
+- args = splitquoted(args)
+- destination_path = args[0]
+-
+- # add to the list of active downloads
+- global ACTIVE_DOWNLOADS
+- ACTIVE_DOWNLOADS[destination_path] = (0.0,)
+-
+- # update the progress
+- update_download_section(uzbl)
+-
+-def download_progress(uzbl, args):
+- # parse the arguments
+- args = splitquoted(args)
+- destination_path = args[0]
+- progress = float(args[1])
+-
+- # update the progress
+- global ACTIVE_DOWNLOADS
+- ACTIVE_DOWNLOADS[destination_path] = (progress,)
+-
+- # update the status bar variable
+- update_download_section(uzbl)
+-
+-def download_complete(uzbl, args):
+- # parse the arguments
+- args = splitquoted(args)
+- destination_path = args[0]
+-
+- # remove from the list of active downloads
+- global ACTIVE_DOWNLOADS
+- del ACTIVE_DOWNLOADS[destination_path]
+-
+- # update the status bar variable
+- update_download_section(uzbl)
+-
+-# plugin init hook
+-def init(uzbl):
+- connect_dict(uzbl, {
+- 'DOWNLOAD_STARTED': download_started,
+- 'DOWNLOAD_PROGRESS': download_progress,
+- 'DOWNLOAD_COMPLETE': download_complete,
+- })
+diff --git a/examples/data/plugins/history.py b/examples/data/plugins/history.py
+deleted file mode 100644
+index f42f86f..0000000
+--- a/examples/data/plugins/history.py
++++ /dev/null
+@@ -1,129 +0,0 @@
+-import random
+-
+-shared_history = {'':[]}
+-
+-class History(object):
+- def __init__(self, uzbl):
+- self.uzbl = uzbl
+- self._temporary = []
+- self.prompt = ''
+- self.cursor = None
+- self.__temp_tail = False
+- self.search_key = None
+-
+- def prev(self):
+- if self.cursor is None:
+- self.cursor = len(self) - 1
+- else:
+- self.cursor -= 1
+-
+- if self.search_key:
+- while self.cursor >= 0 and self.search_key not in self[self.cursor]:
+- self.cursor -= 1
+-
+- if self.cursor < 0 or len(self) == 0:
+- self.cursor = -1
+- return random.choice(end_messages)
+-
+- return self[self.cursor]
+-
+- def next(self):
+- if self.cursor is None:
+- return ''
+-
+- self.cursor += 1
+-
+- if self.search_key:
+- while self.cursor < len(self) and self.search_key not in self[self.cursor]:
+- self.cursor += 1
+-
+- if self.cursor >= len(shared_history[self.prompt]):
+- self.cursor = None
+- self.search_key = None
+-
+- if self._temporary:
+- return self._temporary.pop()
+- return ''
+-
+- return self[self.cursor]
+-
+- def change_prompt(self, prompt):
+- self.prompt = prompt
+- self._temporary = []
+- self.__temp_tail = False
+- if prompt not in shared_history:
+- shared_history[prompt] = []
+-
+- def search(self, key):
+- self.search_key = key
+- self.cursor = None
+-
+- def add(self, cmd):
+- if self._temporary:
+- self._temporary.pop()
+-
+- shared_history[self.prompt].append(cmd)
+- self.cursor = None
+- self.search_key = None
+-
+- def add_temporary(self, cmd):
+- assert not self._temporary
+-
+- self._temporary.append(cmd)
+- self.cursor = len(self) - 1
+-
+- def __getitem__(self, i):
+- if i < len(shared_history[self.prompt]):
+- return shared_history[self.prompt][i]
+- return self._temporary[i-len(shared_history)+1]
+-
+- def __len__(self):
+- return len(shared_history[self.prompt]) + len(self._temporary)
+-
+- def __str__(self):
+- return "(History %s, %s)" % (self.cursor, self.prompt)
+-
+-def keycmd_exec(uzbl, modstate, keylet):
+- cmd = keylet.get_keycmd()
+- if cmd:
+- uzbl.history.add(cmd)
+-
+-def history_prev(uzbl, _x):
+- cmd = uzbl.keylet.get_keycmd()
+- if uzbl.history.cursor is None and cmd:
+- uzbl.history.add_temporary(cmd)
+-
+- uzbl.set_keycmd(uzbl.history.prev())
+- uzbl.logger.debug('PREV %s' % uzbl.history)
+-
+-def history_next(uzbl, _x):
+- cmd = uzbl.keylet.get_keycmd()
+-
+- uzbl.set_keycmd(uzbl.history.next())
+- uzbl.logger.debug('NEXT %s' % uzbl.history)
+-
+-def history_search(uzbl, key):
+- uzbl.history.search(key)
+- uzbl.send('event HISTORY_PREV')
+- uzbl.logger.debug('SEARCH %s %s' % (key, uzbl.history))
+-
+-end_messages = ('Look behind you, A three-headed monkey!', 'error #4: static from nylon underwear.', 'error #5: static from plastic slide rules.', 'error #6: global warming.', 'error #9: doppler effect.', 'error #16: somebody was calculating pi on the server.', 'error #19: floating point processor overflow.', 'error #21: POSIX compliance problem.', 'error #25: Decreasing electron flux.', 'error #26: first Saturday after first full moon in Winter.', 'error #64: CPU needs recalibration.', 'error #116: the real ttys became pseudo ttys and vice-versa.', 'error #229: wrong polarity of neutron flow.', 'error #330: quantum decoherence.', 'error #388: Bad user karma.', 'error #407: Route flapping at the NAP.', 'error #435: Internet shut down due to maintenance.')
+-
+-# plugin init hook
+-def init(uzbl):
+- connect_dict(uzbl, {
+- 'KEYCMD_EXEC': keycmd_exec,
+- 'HISTORY_PREV': history_prev,
+- 'HISTORY_NEXT': history_next,
+- 'HISTORY_SEARCH': history_search
+- })
+-
+- export_dict(uzbl, {
+- 'history' : History(uzbl)
+- })
+-
+-# plugin after hook
+-def after(uzbl):
+- uzbl.on_set('keycmd_prompt', lambda uzbl, k, v: uzbl.history.change_prompt(v))
+-
+-# vi: set et ts=4:
+diff --git a/examples/data/plugins/keycmd.py b/examples/data/plugins/keycmd.py
+deleted file mode 100644
+index 1bb70e3..0000000
+--- a/examples/data/plugins/keycmd.py
++++ /dev/null
+@@ -1,423 +0,0 @@
+-import re
+-
+-# Keycmd format which includes the markup for the cursor.
+-KEYCMD_FORMAT = "%s<span @cursor_style>%s</span>%s"
+-MODCMD_FORMAT = "<span> %s </span>"
+-
+-
+-def escape(str):
+- for char in ['\\', '@']:
+- str = str.replace(char, '\\'+char)
+-
+- return str
+-
+-
+-def uzbl_escape(str):
+- return "@[%s]@" % escape(str) if str else ''
+-
+-
+-class Keylet(object):
+- '''Small per-instance object that tracks characters typed.'''
+-
+- def __init__(self):
+- # Modcmd tracking
+- self.modcmd = ''
+- self.is_modcmd = False
+-
+- # Keycmd tracking
+- self.keycmd = ''
+- self.cursor = 0
+-
+- self.modmaps = {}
+- self.ignores = {}
+-
+-
+- def get_keycmd(self):
+- '''Get the keycmd-part of the keylet.'''
+-
+- return self.keycmd
+-
+-
+- def get_modcmd(self):
+- '''Get the modcmd-part of the keylet.'''
+-
+- if not self.is_modcmd:
+- return ''
+-
+- return self.modcmd
+-
+-
+- def modmap_key(self, key):
+- '''Make some obscure names for some keys friendlier.'''
+-
+- if key in self.modmaps:
+- return self.modmaps[key]
+-
+- elif key.endswith('_L') or key.endswith('_R'):
+- # Remove left-right discrimination and try again.
+- return self.modmap_key(key[:-2])
+-
+- else:
+- return key
+-
+-
+- def key_ignored(self, key):
+- '''Check if the given key is ignored by any ignore rules.'''
+-
+- for (glob, match) in self.ignores.items():
+- if match(key):
+- return True
+-
+- return False
+-
+-
+- def __repr__(self):
+- '''Return a string representation of the keylet.'''
+-
+- l = []
+- if self.is_modcmd:
+- l.append('modcmd=%r' % self.get_modcmd())
+-
+- if self.keycmd:
+- l.append('keycmd=%r' % self.get_keycmd())
+-
+- return '<keylet(%s)>' % ', '.join(l)
+-
+-
+-def add_modmap(uzbl, key, map):
+- '''Add modmaps.
+-
+- Examples:
+- set modmap = request MODMAP
+- @modmap <Control> <Ctrl>
+- @modmap <ISO_Left_Tab> <Shift-Tab>
+- ...
+-
+- Then:
+- @bind <Shift-Tab> = <command1>
+- @bind <Ctrl>x = <command2>
+- ...
+-
+- '''
+-
+- assert len(key)
+- modmaps = uzbl.keylet.modmaps
+-
+- modmaps[key.strip('<>')] = map.strip('<>')
+- uzbl.event("NEW_MODMAP", key, map)
+-
+-
+-def modmap_parse(uzbl, map):
+- '''Parse a modmap definiton.'''
+-
+- split = [s.strip() for s in map.split(' ') if s.split()]
+-
+- if not split or len(split) > 2:
+- raise Exception('Invalid modmap arugments: %r' % map)
+-
+- add_modmap(uzbl, *split)
+-
+-
+-def add_key_ignore(uzbl, glob):
+- '''Add an ignore definition.
+-
+- Examples:
+- set ignore_key = request IGNORE_KEY
+- @ignore_key <Shift>
+- @ignore_key <ISO_*>
+- ...
+- '''
+-
+- assert len(glob) > 1
+- ignores = uzbl.keylet.ignores
+-
+- glob = "<%s>" % glob.strip("<> ")
+- restr = glob.replace('*', '[^\s]*')
+- match = re.compile(restr).match
+-
+- ignores[glob] = match
+- uzbl.event('NEW_KEY_IGNORE', glob)
+-
+-
+-def clear_keycmd(uzbl, *args):
+- '''Clear the keycmd for this uzbl instance.'''
+-
+- k = uzbl.keylet
+- k.keycmd = ''
+- k.cursor = 0
+- del uzbl.config['keycmd']
+- uzbl.event('KEYCMD_CLEARED')
+-
+-
+-def clear_modcmd(uzbl):
+- '''Clear the modcmd for this uzbl instance.'''
+-
+- k = uzbl.keylet
+- k.modcmd = ''
+- k.is_modcmd = False
+-
+- del uzbl.config['modcmd']
+- uzbl.event('MODCMD_CLEARED')
+-
+-
+-def clear_current(uzbl):
+- '''Clear the modcmd if is_modcmd else clear keycmd.'''
+-
+- if uzbl.keylet.is_modcmd:
+- clear_modcmd(uzbl)
+-
+- else:
+- clear_keycmd(uzbl)
+-
+-
+-def update_event(uzbl, modstate, k, execute=True):
+- '''Raise keycmd & modcmd update events.'''
+-
+- keycmd, modcmd = k.get_keycmd(), ''.join(modstate) + k.get_modcmd()
+-
+- if k.is_modcmd:
+- logger.debug('modcmd_update, %s' % modcmd)
+- uzbl.event('MODCMD_UPDATE', modstate, k)
+-
+- else:
+- logger.debug('keycmd_update, %s' % keycmd)
+- uzbl.event('KEYCMD_UPDATE', modstate, k)
+-
+- if uzbl.config.get('modcmd_updates', '1') == '1':
+- new_modcmd = ''.join(modstate) + k.get_modcmd()
+- if not new_modcmd or not k.is_modcmd:
+- del uzbl.config['modcmd']
+-
+- elif new_modcmd == modcmd:
+- uzbl.config['modcmd'] = MODCMD_FORMAT % uzbl_escape(modcmd)
+-
+- if uzbl.config.get('keycmd_events', '1') != '1':
+- return
+-
+- new_keycmd = k.get_keycmd()
+- if not new_keycmd:
+- del uzbl.config['keycmd']
+-
+- elif new_keycmd == keycmd:
+- # Generate the pango markup for the cursor in the keycmd.
+- curchar = keycmd[k.cursor] if k.cursor < len(keycmd) else ' '
+- chunks = [keycmd[:k.cursor], curchar, keycmd[k.cursor+1:]]
+- value = KEYCMD_FORMAT % tuple(map(uzbl_escape, chunks))
+-
+- uzbl.config['keycmd'] = value
+-
+-
+-def inject_str(str, index, inj):
+- '''Inject a string into string at at given index.'''
+-
+- return "%s%s%s" % (str[:index], inj, str[index:])
+-
+-
+-def parse_key_event(uzbl, key):
+- ''' Build a set from the modstate part of the event, and pass all keys through modmap '''
+- keylet = uzbl.keylet
+-
+- modstate, key = splitquoted(key)
+- modstate = set(['<%s>' % keylet.modmap_key(k) for k in modstate.split('|') if k])
+-
+- key = keylet.modmap_key(key)
+- return modstate, key
+-
+-
+-def key_press(uzbl, key):
+- '''Handle KEY_PRESS events. Things done by this function include:
+-
+- 1. Ignore all shift key presses (shift can be detected by capital chars)
+- 2. In non-modcmd mode:
+- a. append char to keycmd
+- 3. If not in modcmd mode and a modkey was pressed set modcmd mode.
+- 4. Keycmd is updated and events raised if anything is changed.'''
+-
+- k = uzbl.keylet
+- modstate, key = parse_key_event(uzbl, key)
+- k.is_modcmd = any(not k.key_ignored(m) for m in modstate)
+-
+- logger.debug('key press modstate=%s' % str(modstate))
+- if key.lower() == 'space' and not k.is_modcmd and k.keycmd:
+- k.keycmd = inject_str(k.keycmd, k.cursor, ' ')
+- k.cursor += 1
+-
+- elif not k.is_modcmd and len(key) == 1:
+- if uzbl.config.get('keycmd_events', '1') != '1':
+- # TODO, make a note on what's going on here
+- k.keycmd = ''
+- k.cursor = 0
+- del uzbl.config['keycmd']
+- return
+-
+- k.keycmd = inject_str(k.keycmd, k.cursor, key)
+- k.cursor += 1
+-
+- elif len(key) == 1:
+- k.modcmd += key
+-
+- else:
+- if not k.key_ignored('<%s>' % key):
+- modstate.add('<%s>' % key)
+- k.is_modcmd = True
+-
+- update_event(uzbl, modstate, k)
+-
+-
+-def key_release(uzbl, key):
+- '''Respond to KEY_RELEASE event. Things done by this function include:
+-
+- 1. If in a mod-command then raise a MODCMD_EXEC.
+- 2. Update the keycmd uzbl variable if anything changed.'''
+- k = uzbl.keylet
+- modstate, key = parse_key_event(uzbl, key)
+-
+- if len(key) > 1:
+- if k.is_modcmd:
+- uzbl.event('MODCMD_EXEC', modstate, k)
+-
+- clear_modcmd(uzbl)
+-
+-
+-def set_keycmd(uzbl, keycmd):
+- '''Allow setting of the keycmd externally.'''
+-
+- k = uzbl.keylet
+- k.keycmd = keycmd
+- k.cursor = len(keycmd)
+- update_event(uzbl, set(), k, False)
+-
+-
+-def inject_keycmd(uzbl, keycmd):
+- '''Allow injecting of a string into the keycmd at the cursor position.'''
+-
+- k = uzbl.keylet
+- k.keycmd = inject_str(k.keycmd, k.cursor, keycmd)
+- k.cursor += len(keycmd)
+- update_event(uzbl, set(), k, False)
+-
+-
+-def append_keycmd(uzbl, keycmd):
+- '''Allow appening of a string to the keycmd.'''
+-
+- k = uzbl.keylet
+- k.keycmd += keycmd
+- k.cursor = len(k.keycmd)
+- update_event(uzbl, set(), k, False)
+-
+-
+-def keycmd_strip_word(uzbl, seps):
+- ''' Removes the last word from the keycmd, similar to readline ^W '''
+-
+- seps = seps or ' '
+- k = uzbl.keylet
+- if not k.keycmd:
+- return
+-
+- head, tail = k.keycmd[:k.cursor].rstrip(seps), k.keycmd[k.cursor:]
+- rfind = -1
+- for sep in seps:
+- p = head.rfind(sep)
+- if p >= 0 and rfind < p + 1:
+- rfind = p + 1
+- if rfind == len(head) and head[-1] in seps:
+- rfind -= 1
+- head = head[:rfind] if rfind + 1 else ''
+- k.keycmd = head + tail
+- k.cursor = len(head)
+- update_event(uzbl, set(), k, False)
+-
+-
+-def keycmd_backspace(uzbl, *args):
+- '''Removes the character at the cursor position in the keycmd.'''
+-
+- k = uzbl.keylet
+- if not k.keycmd or not k.cursor:
+- return
+-
+- k.keycmd = k.keycmd[:k.cursor-1] + k.keycmd[k.cursor:]
+- k.cursor -= 1
+- update_event(uzbl, set(), k, False)
+-
+-
+-def keycmd_delete(uzbl, *args):
+- '''Removes the character after the cursor position in the keycmd.'''
+-
+- k = uzbl.keylet
+- if not k.keycmd:
+- return
+-
+- k.keycmd = k.keycmd[:k.cursor] + k.keycmd[k.cursor+1:]
+- update_event(uzbl, set(), k, False)
+-
+-
+-def keycmd_exec_current(uzbl, *args):
+- '''Raise a KEYCMD_EXEC with the current keylet and then clear the
+- keycmd.'''
+-
+- uzbl.event('KEYCMD_EXEC', set(), uzbl.keylet)
+- clear_keycmd(uzbl)
+-
+-
+-def set_cursor_pos(uzbl, index):
+- '''Allow setting of the cursor position externally. Supports negative
+- indexing and relative stepping with '+' and '-'.'''
+-
+- k = uzbl.keylet
+- if index == '-':
+- cursor = k.cursor - 1
+-
+- elif index == '+':
+- cursor = k.cursor + 1
+-
+- else:
+- cursor = int(index.strip())
+- if cursor < 0:
+- cursor = len(k.keycmd) + cursor + 1
+-
+- if cursor < 0:
+- cursor = 0
+-
+- if cursor > len(k.keycmd):
+- cursor = len(k.keycmd)
+-
+- k.cursor = cursor
+- update_event(uzbl, set(), k, False)
+-
+-
+-# plugin init hook
+-def init(uzbl):
+- '''Export functions and connect handlers to events.'''
+-
+- connect_dict(uzbl, {
+- 'APPEND_KEYCMD': append_keycmd,
+- 'IGNORE_KEY': add_key_ignore,
+- 'INJECT_KEYCMD': inject_keycmd,
+- 'KEYCMD_BACKSPACE': keycmd_backspace,
+- 'KEYCMD_DELETE': keycmd_delete,
+- 'KEYCMD_EXEC_CURRENT': keycmd_exec_current,
+- 'KEYCMD_STRIP_WORD': keycmd_strip_word,
+- 'KEYCMD_CLEAR': clear_keycmd,
+- 'KEY_PRESS': key_press,
+- 'KEY_RELEASE': key_release,
+- 'MOD_PRESS': key_press,
+- 'MOD_RELEASE': key_release,
+- 'MODMAP': modmap_parse,
+- 'SET_CURSOR_POS': set_cursor_pos,
+- 'SET_KEYCMD': set_keycmd,
+- })
+-
+- export_dict(uzbl, {
+- 'add_key_ignore': add_key_ignore,
+- 'add_modmap': add_modmap,
+- 'append_keycmd': append_keycmd,
+- 'clear_current': clear_current,
+- 'clear_keycmd': clear_keycmd,
+- 'clear_modcmd': clear_modcmd,
+- 'inject_keycmd': inject_keycmd,
+- 'keylet': Keylet(),
+- 'set_cursor_pos': set_cursor_pos,
+- 'set_keycmd': set_keycmd,
+- })
+-
+-# vi: set et ts=4:
+diff --git a/examples/data/plugins/mode.py b/examples/data/plugins/mode.py
+deleted file mode 100644
+index e0de706..0000000
+--- a/examples/data/plugins/mode.py
++++ /dev/null
+@@ -1,68 +0,0 @@
+-from collections import defaultdict
+-
+-def parse_mode_config(uzbl, args):
+- '''Parse `MODE_CONFIG <mode> <var> = <value>` event and update config if
+- the `<mode>` is the current mode.'''
+-
+- ustrip = unicode.strip
+- args = unicode(args)
+-
+- assert args.strip(), "missing mode config args"
+- (mode, args) = map(ustrip, (args.strip().split(' ', 1) + ['',])[:2])
+-
+- assert args.strip(), "missing mode config set arg"
+- (key, value) = map(ustrip, (args.strip().split('=', 1) + [None,])[:2])
+- assert key and value is not None, "invalid mode config set syntax"
+-
+- uzbl.mode_config[mode][key] = value
+- if uzbl.config.get('mode', None) == mode:
+- uzbl.config[key] = value
+-
+-
+-def default_mode_updated(uzbl, var, mode):
+- if mode and not uzbl.config.get('mode', None):
+- logger.debug('setting mode to default %r' % mode)
+- uzbl.config['mode'] = mode
+-
+-
+-def mode_updated(uzbl, var, mode):
+- if not mode:
+- mode = uzbl.config.get('default_mode', 'command')
+- logger.debug('setting mode to default %r' % mode)
+- uzbl.config['mode'] = mode
+- return
+-
+- # Load mode config
+- mode_config = uzbl.mode_config.get(mode, None)
+- if mode_config:
+- uzbl.config.update(mode_config)
+-
+- uzbl.send('event MODE_CONFIRM %s' % mode)
+-
+-
+-def confirm_change(uzbl, mode):
+- if mode and uzbl.config.get('mode', None) == mode:
+- uzbl.event('MODE_CHANGED', mode)
+-
+-
+-# plugin init hook
+-def init(uzbl):
+- require('config')
+- require('on_set')
+-
+- # Usage `uzbl.mode_config[mode][key] = value`
+- export(uzbl, 'mode_config', defaultdict(dict))
+-
+- connect_dict(uzbl, {
+- 'MODE_CONFIG': parse_mode_config,
+- 'MODE_CONFIRM': confirm_change,
+- })
+-
+-# plugin after hook
+-def after(uzbl):
+- uzbl.on_set('mode', mode_updated)
+- uzbl.on_set('default_mode', default_mode_updated)
+-
+-# plugin cleanup hook
+-def cleanup(uzbl):
+- uzbl.mode_config.clear()
+diff --git a/examples/data/plugins/on_event.py b/examples/data/plugins/on_event.py
+deleted file mode 100644
+index 32f09e2..0000000
+--- a/examples/data/plugins/on_event.py
++++ /dev/null
+@@ -1,88 +0,0 @@
+-'''Plugin provides arbitrary binding of uzbl events to uzbl commands.
+-
+-Formatting options:
+- %s = space separated string of the arguments
+- %r = escaped and quoted version of %s
+- %1 = argument 1
+- %2 = argument 2
+- %n = argument n
+-
+-Usage:
+- request ON_EVENT LINK_HOVER set selected_uri = $1
+- --> LINK_HOVER http://uzbl.org/
+- <-- set selected_uri = http://uzbl.org/
+-
+- request ON_EVENT CONFIG_CHANGED print Config changed: %1 = %2
+- --> CONFIG_CHANGED selected_uri http://uzbl.org/
+- <-- print Config changed: selected_uri = http://uzbl.org/
+-'''
+-
+-import sys
+-import re
+-
+-def event_handler(uzbl, *args, **kargs):
+- '''This function handles all the events being watched by various
+- on_event definitions and responds accordingly.'''
+-
+- # Could be connected to a EM internal event that can use anything as args
+- if len(args) == 1 and isinstance(args[0], basestring):
+- args = splitquoted(args[0])
+-
+- events = uzbl.on_events
+- event = kargs['on_event']
+- if event not in events:
+- return
+-
+- commands = events[event]
+- cmd_expand = uzbl.cmd_expand
+- for cmd in commands:
+- cmd = cmd_expand(cmd, args)
+- uzbl.send(cmd)
+-
+-
+-def on_event(uzbl, event, cmd):
+- '''Add a new event to watch and respond to.'''
+-
+- event = event.upper()
+- events = uzbl.on_events
+- if event not in events:
+- connect(uzbl, event, event_handler, on_event=event)
+- events[event] = []
+-
+- cmds = events[event]
+- if cmd not in cmds:
+- cmds.append(cmd)
+-
+-
+-def parse_on_event(uzbl, args):
+- '''Parse ON_EVENT events and pass them to the on_event function.
+-
+- Syntax: "event ON_EVENT <EVENT_NAME> commands".'''
+-
+- args = args.strip()
+- assert args, 'missing on event arguments'
+-
+- (event, command) = (args.split(' ', 1) + ['',])[:2]
+- assert event and command, 'missing on event command'
+- on_event(uzbl, event, command)
+-
+-
+-# plugin init hook
+-def init(uzbl):
+- '''Export functions and connect handlers to events.'''
+-
+- connect(uzbl, 'ON_EVENT', parse_on_event)
+-
+- export_dict(uzbl, {
+- 'on_event': on_event,
+- 'on_events': {},
+- })
+-
+-# plugin cleanup hook
+-def cleanup(uzbl):
+- for handlers in uzbl.on_events.values():
+- del handlers[:]
+-
+- uzbl.on_events.clear()
+-
+-# vi: set et ts=4:
+diff --git a/examples/data/plugins/on_set.py b/examples/data/plugins/on_set.py
+deleted file mode 100644
+index 130b816..0000000
+--- a/examples/data/plugins/on_set.py
++++ /dev/null
+@@ -1,92 +0,0 @@
+-from re import compile
+-from functools import partial
+-
+-valid_glob = compile('^[A-Za-z0-9_\*\.]+$').match
+-
+-def make_matcher(glob):
+- '''Make matcher function from simple glob.'''
+-
+- pattern = "^%s$" % glob.replace('*', '[^\s]*')
+- return compile(pattern).match
+-
+-
+-def exec_handlers(uzbl, handlers, key, arg):
+- '''Execute the on_set handlers that matched the key.'''
+-
+- for handler in handlers:
+- if callable(handler):
+- handler(key, arg)
+-
+- else:
+- uzbl.send(uzbl.cmd_expand(handler, [key, arg]))
+-
+-
+-def check_for_handlers(uzbl, key, arg):
+- '''Check for handlers for the current key.'''
+-
+- for (matcher, handlers) in uzbl.on_sets.values():
+- if matcher(key):
+- exec_handlers(uzbl, handlers, key, arg)
+-
+-
+-def on_set(uzbl, glob, handler, prepend=True):
+- '''Add a new handler for a config key change.
+-
+- Structure of the `uzbl.on_sets` dict:
+- { glob : ( glob matcher function, handlers list ), .. }
+- '''
+-
+- assert valid_glob(glob)
+-
+- while '**' in glob:
+- glob = glob.replace('**', '*')
+-
+- if callable(handler):
+- orig_handler = handler
+- if prepend:
+- handler = partial(handler, uzbl)
+-
+- else:
+- orig_handler = handler = unicode(handler)
+-
+- if glob in uzbl.on_sets:
+- (matcher, handlers) = uzbl.on_sets[glob]
+- handlers.append(handler)
+-
+- else:
+- matcher = make_matcher(glob)
+- uzbl.on_sets[glob] = (matcher, [handler,])
+-
+- uzbl.logger.info('on set %r call %r' % (glob, orig_handler))
+-
+-
+-def parse_on_set(uzbl, args):
+- '''Parse `ON_SET <glob> <command>` event then pass arguments to the
+- `on_set(..)` function.'''
+-
+- (glob, command) = (args.split(' ', 1) + [None,])[:2]
+- assert glob and command and valid_glob(glob)
+- on_set(uzbl, glob, command)
+-
+-
+-# plugins init hook
+-def init(uzbl):
+- require('config')
+- require('cmd_expand')
+-
+- export_dict(uzbl, {
+- 'on_sets': {},
+- 'on_set': on_set,
+- })
+-
+- connect_dict(uzbl, {
+- 'ON_SET': parse_on_set,
+- 'CONFIG_CHANGED': check_for_handlers,
+- })
+-
+-# plugins cleanup hook
+-def cleanup(uzbl):
+- for (matcher, handlers) in uzbl.on_sets.values():
+- del handlers[:]
+-
+- uzbl.on_sets.clear()
+diff --git a/examples/data/plugins/progress_bar.py b/examples/data/plugins/progress_bar.py
+deleted file mode 100644
+index b2edffc..0000000
+--- a/examples/data/plugins/progress_bar.py
++++ /dev/null
+@@ -1,93 +0,0 @@
+-UPDATES = 0
+-
+-def update_progress(uzbl, progress=None):
+- '''Updates the progress.output variable on LOAD_PROGRESS update.
+-
+- The current substitution options are:
+- %d = done char * done
+- %p = pending char * remaining
+- %c = percent done
+- %i = int done
+- %s = -\|/ spinner
+- %t = percent pending
+- %o = int pending
+- %r = sprites
+-
+- Default configuration options:
+- progress.format = [%d>%p]%c
+- progress.width = 8
+- progress.done = =
+- progress.pending =
+- progress.spinner = -\|/
+- progress.sprites = loading
+- '''
+-
+- global UPDATES
+-
+- if progress is None:
+- UPDATES = 0
+- progress = 100
+-
+- else:
+- UPDATES += 1
+- progress = int(progress)
+-
+- # Get progress config vars.
+- format = uzbl.config.get('progress.format', '[%d>%p]%c')
+- width = int(uzbl.config.get('progress.width', 8))
+- done_symbol = uzbl.config.get('progress.done', '=')
+- pend = uzbl.config.get('progress.pending', None)
+- pending_symbol = pend if pend else ' '
+-
+- # Inflate the done and pending bars to stop the progress bar
+- # jumping around.
+- if '%c' in format or '%i' in format:
+- count = format.count('%c') + format.count('%i')
+- width += (3-len(str(progress))) * count
+-
+- if '%t' in format or '%o' in format:
+- count = format.count('%t') + format.count('%o')
+- width += (3-len(str(100-progress))) * count
+-
+- done = int(((progress/100.0)*width)+0.5)
+- pending = width - done
+-
+- if '%d' in format:
+- format = format.replace('%d', done_symbol * done)
+-
+- if '%p' in format:
+- format = format.replace('%p', pending_symbol * pending)
+-
+- if '%c' in format:
+- format = format.replace('%c', '%d%%' % progress)
+-
+- if '%i' in format:
+- format = format.replace('%i', '%d' % progress)
+-
+- if '%t' in format:
+- format = format.replace('%t', '%d%%' % (100-progress))
+-
+- if '%o' in format:
+- format = format.replace('%o', '%d' % (100-progress))
+-
+- if '%s' in format:
+- spinner = uzbl.config.get('progress.spinner', '-\\|/')
+- index = 0 if progress == 100 else UPDATES % len(spinner)
+- spin = '\\\\' if spinner[index] == '\\' else spinner[index]
+- format = format.replace('%s', spin)
+-
+- if '%r' in format:
+- sprites = uzbl.config.get('progress.sprites', 'loading')
+- index = int(((progress/100.0)*len(sprites))+0.5)-1
+- sprite = '\\\\' if sprites[index] == '\\' else sprites[index]
+- format = format.replace('%r', sprite)
+-
+- if uzbl.config.get('progress.output', None) != format:
+- uzbl.config['progress.output'] = format
+-
+-# plugin init hook
+-def init(uzbl):
+- connect_dict(uzbl, {
+- 'LOAD_COMMIT': lambda uzbl, uri: update_progress(uzbl),
+- 'LOAD_PROGRESS': update_progress,
+- })
+diff --git a/examples/data/scripts/auth.py b/examples/data/scripts/auth.py
+index 49fa41e..dbf58dc 100755
+--- a/examples/data/scripts/auth.py
++++ b/examples/data/scripts/auth.py
+@@ -1,5 +1,6 @@
+ #!/usr/bin/env python
+
++import os
+ import gtk
+ import sys
+
+@@ -41,13 +42,20 @@ def getText(authInfo, authHost, authRealm):
+ dialog.show_all()
+ rv = dialog.run()
+
+- output = login.get_text() + "\n" + password.get_text()
++ output = {
++ 'username': login.get_text(),
++ 'password': password.get_text()
++ }
+ dialog.destroy()
+ return rv, output
+
<Skipped 7294 lines>
================================================================
---- gitweb:
http://git.pld-linux.org/gitweb.cgi/packages/uzbl.git/commitdiff/d2f1f677cbeb64f3e2b067e74398ad03d017304a
More information about the pld-cvs-commit
mailing list