diff options
author | ben <benjaminfranzke@gmail.com> | 2010-01-04 21:04:41 +0100 |
---|---|---|
committer | ben <benjaminfranzke@gmail.com> | 2010-01-04 21:04:41 +0100 |
commit | fff9c1bbfa6b120c6fd349f95b0e74c618ebb6ea (patch) | |
tree | 9e88a6ffcc78bd148410b7f1ea5e21af624c5d00 | |
parent | 08a86f4d3f64917caad9ecb791e2b4f84a498fdc (diff) | |
download | dotfiles-fff9c1bbfa6b120c6fd349f95b0e74c618ebb6ea.tar.gz dotfiles-fff9c1bbfa6b120c6fd349f95b0e74c618ebb6ea.tar.bz2 dotfiles-fff9c1bbfa6b120c6fd349f95b0e74c618ebb6ea.zip |
add uzbl configuration
28 files changed, 3729 insertions, 0 deletions
diff --git a/.config/uzbl/bookmarks b/.config/uzbl/bookmarks new file mode 100644 index 0000000..13fcd48 --- /dev/null +++ b/.config/uzbl/bookmarks @@ -0,0 +1,4 @@ +http://www.archlinux.org linux arch +http://www.uzbl.org uzbl browser +http://dieter.plaetinck.be uzbl +http://www.icanhascheezburger.com lolcats fun diff --git a/.config/uzbl/config b/.config/uzbl/config new file mode 100644 index 0000000..47d8839 --- /dev/null +++ b/.config/uzbl/config @@ -0,0 +1,121 @@ +# example uzbl config. +# all settings are optional. you can use uzbl without any config at all (but it won't do much) + +# keyboard behavior in this sample config is sort of vimstyle + +# Handlers +set download_handler = spawn $XDG_DATA_HOME/uzbl/scripts/download.sh +set cookie_handler = spawn $XDG_DATA_HOME/uzbl/scripts/cookies.py +#set new_window = sh 'echo uri "$8" > $4' # open in same window +set new_window = sh 'uzbl -u $8' # equivalent to the default behaviour +set scheme_handler = spawn $XDG_DATA_HOME/uzbl/scripts/scheme.py +set load_start_handler = chain 'set keycmd = ' 'set status_message = <span foreground="khaki">wait</span>' +set load_commit_handler = set status_message = <span foreground="green">recv</span> +set load_finish_handler = chain 'set status_message = <span foreground="gold">done</span>' 'spawn $XDG_DATA_HOME/uzbl/scripts/history.sh' + + + +# Behaviour and appearance +set show_status = 1 +set status_background = #303030 +set status_format = <span font_family="monospace"><span background="khaki" foreground="black">[\@[\@MODE]\@]</span> [<span weight="bold" foreground="red">\@[\@keycmd]\@</span>] <span foreground="#606060"> \@[\@LOAD_PROGRESSBAR]\@ </span><span foreground="#99FF66">\@[\@uri]\@</span> <span foreground="khaki">\@[\@NAME]\@</span> <span foreground="orange">\@status_message</span><span foreground="#606060"> \@[\@SELECTED_URI]\@</span></span> +set status_top = 0 +set insert_indicator = I +set command_indicator = C +set useragent = Uzbl (Webkit @WEBKIT_MAJOR.@WEBKIT_MINOR.@WEBKIT_MICRO) (@(uname -o)@ @(uname -m)@ [@ARCH_UZBL]) (Commit @COMMIT) + +set fifo_dir = /tmp +set socket_dir = /tmp +set shell_cmd = sh -c + +# Keyboard interface +set modkey = Mod1 +# like this you can enter any command at runtime, interactively. prefixed by ':' +bind :_ = chain '%s' + +bind j = scroll_vert 20 +bind k = scroll_vert -20 +bind h = scroll_horz -20 +bind l = scroll_horz 20 +bind << = scroll_begin +bind >> = scroll_end +bind b = back +bind m = forward +bind S = stop +bind r = reload +bind R = reload_ign_cache +bind + = zoom_in +bind - = zoom_out +bind T = toggle_zoom_type +bind 1 = sh "echo set zoom_level = 1.0 > $4" +bind 2 = sh "echo set zoom_level = 2.0 > $4" +bind t = toggle_status +bind /* = search %s +bind ?* = search_reverse %s +#jump to next +bind n = search +bind N = search_reverse +bind gh = uri http://www.uzbl.org +# shortcut to set the uri. TODO: i think we can abandon the uri command in favor of 'set uri = ..' +bind o _ = uri %s +# shortcut to set variables +bind s _ = set %s +bind \wiki _ = uri http://wiki.archlinux.org/index.php/Special:Search?search=%s&go=Go +bind gg _ = uri http://www.google.com/search?q=%s +bind i = toggle_insert_mode +# disable insert mode (1 to enable). note that Esc works to disable, regardless of this setting +bind I = toggle_insert_mode 0 +# Enclose the executable in quotes if it has spaces. Any additional parameters you use will +# appear AFTER the default parameters +bind B = spawn $XDG_DATA_HOME/uzbl/scripts/insert_bookmark.sh +bind U = spawn $XDG_DATA_HOME/uzbl/scripts/load_url_from_history.sh +bind u = spawn $XDG_DATA_HOME/uzbl/scripts/load_url_from_bookmarks.sh +# with the sample yank script, you can yank one of the arguments into clipboard/selection +bind yurl = spawn $XDG_DATA_HOME/uzbl/scripts/yank.sh 6 primary +bind ytitle = spawn $XDG_DATA_HOME/uzbl/scripts/yank.sh 7 clipboard +# does the same as yurl but without needing a script +bind y2url = sh 'echo -n $6 | xclip' +# go the page from primary selection +bind p = sh 'echo "uri `xclip -selection primary -o`" > $4' +# go to the page in clipboard +bind P = sh 'echo "uri `xclip -selection clipboard -o`" > $4' +# start a new uzbl instance from the page in primary selection +bind 'p = sh 'exec uzbl --uri $(xclip -o)' +bind ZZ = exit +bind Xs = js alert("hi"); +# example showing how to use sh +# it sends a command to the fifo, whose path is told via a positional param +# if fifo_dir is not set, it'll echo to a file named (null) somewhere >:) remember to delete it +# The body of the shell command should be one parameter, so if it has spaces like here, +# you must enclose it in quotes. Remember to escape (and double-escape) quotes and backslashes +# in the body. Any additional parameters you use will appear AFTER the default parameters (cfg file +# path, fifo & socket dirs, etc.) +bind XS = sh 'echo "js alert (\\"This is sent by the shell via a fifo\\")" > "$4"' + +bind !dump = sh "echo dump_config > $4" +bind !reload = sh 'cat $1 > $4' + +# this script allows you to configure (per domain) values to fill in form fields (eg login information) and to fill in these values automatically +bind za = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.sh +bind ze = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.sh edit +bind zn = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.sh new +bind zl = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.sh load + +# other - more advanced - implementation using perl: (could not get this to run - Dieter ) +bind LL = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.pl load +bind LN = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.pl new +bind LE = spawn $XDG_DATA_HOME/uzbl/scripts/formfiller.pl edit + +# we ship some javascripts to do keyboard based link hinting/following. (webkit does not have C DOM bindings yet) +# this is similar to how it works in vimperator (and konqueror) +# TODO: did we resolve: "no click() event for hyperlinks so no referrer set" ? +#hit F to toggle the Hints (now in form of link numbering) +bind F = script $XDG_DATA_HOME/uzbl/scripts/hint.js +# the most stable version: +bind fl* = script $XDG_DATA_HOME/uzbl/scripts/follow_Numbers.js %s +# using strings, not polished yet: +bind fL* = script $XDG_DATA_HOME/uzbl/scripts/follow_Numbers_Strings.js %s + + +# "home" page if you will +set uri = uzbl.org diff --git a/.config/uzbl/cookies b/.config/uzbl/cookies new file mode 100644 index 0000000..9b7374a --- /dev/null +++ b/.config/uzbl/cookies @@ -0,0 +1,22 @@ +# This file demonstrates how one *could* manage his cookies. this file is used by the example cookie handler script. +# stick to this format. +# trusted -> always store what we get, send what we have (TODO: by default, or when requested?) +# deny -> deny storing + sending + +# if you don't like to edit this file manually, you could even write a script that adds/removes entries using sed, and call the script from uzbl with a keybind... + + +TRUSTED +bbs.archlinux.org +archlinux.org +linux.com + + + + +DENY +www.icanhascheezburger.com + + + +# rest -> ask
\ No newline at end of file diff --git a/.config/uzbl/cookies.txt b/.config/uzbl/cookies.txt new file mode 100644 index 0000000..01b900b --- /dev/null +++ b/.config/uzbl/cookies.txt @@ -0,0 +1,17 @@ + # Netscape HTTP Cookie File + # http://www.netscape.com/newsref/std/cookie_spec.html + # This is a generated file! Do not edit. + +.doubleclick.net TRUE / FALSE 1259497519 test_cookie CheckForPermission +.google.com TRUE / FALSE 1319445778 PREF ID=57d7b3e6ee44dd98:TM=1256373778:LM=1256373778:S=f4EUwEgD33-bSdx3 +.google.de TRUE / FALSE 1272187286 NID 28=Qi11_cBLgQd3WrtxinBbHUvFiCdS9iUxoW4CuAt1gBkYFSBHFzDV6tzgQN-nc6LGzGKVmhXNUS-DHZqI-el0HkT3GCo4hFyEhPq-tuoRoHDyE4V7QwWn9UROyY0SCdIX +.google.de TRUE / FALSE 1319448069 PREF ID=95f50e19f59a926d:U=ef9a2a603c860955:TM=1256373653:LM=1256376069:IG=3:S=djxWYEYsFXiww1A6 +.google.de TRUE /verify FALSE 1275307811 SNID 29=pordeA9raH2zGXlEdTj_P--E3g3thcJc2Jlp9pL9IQ=WrJLkb6makf5sarh +.www.google.de TRUE /ig FALSE 1319448076 IGTP TP=H4sIAAAAAAAAAIWTvW_TQBjGfXYKrvtBQEIqTFGQMjSpz7VUlUZKKwZE1KGqRAeWqrr4XjnXnGPLd_loJyaGUhj4KzpDBybUgX-AAoINiZ0ZCSGB7-wo3XrDO9zpnt_zvPeebSy9eP3yW6lyquq2sCtLVsVozz77-OvvZSkx3qL9ufLzT69K9_ecra6USRPj0WjkhnEccnCDOMIsxFFMBxwEPooHctCBA5Yf46E_2XLHES__Nuu-I4CkQXcP0qhpNOYvkLH9FWVUlFGdKfUDKrDnyGlcy5Wk04eRUJC6b3Mi5MEg5c1xcZEcknFxkyRM6NtqDwtIhyzIBHJTWIlsDVurrlfrMgqtFDiRQGs9OGqxvoS0T_gKC1cKXq0PtEWhJuOEBa1uLRXHLU7SEPJcd-3ZJbOClo323CSY2t7L0ppZ2vlp2s0i7Jqzcm1WmjmSLMo7eop2T5GG7dj3spdDyxt-s3pFQyQMQuAuBSyCLifhMTAOfZwKgb3Gmr-x2mh4ntKqtheuutzRk2C2F6cuHxYusVMvCJP-uSPWYwlQRhQoMztKxCDo5h7H2h_Vb4zat6Z6O4XeY8cv9CIlQTtujwU9SLXpBAJGOA4JDUHi4kDJ_rDq_k0SyIPsMZrIy1u-r3trtstTypOCsul4V_rCSSdhYwb5MAQkSggL-9kkxTTWRTNo3beGkDbRIy2_q7_KCTIpvFHlDJnv0Hs0c4EWzmbsc3SJSizkvT_ogW3ccZ5KkkoBTEL1tuixTDzIJlOwQEl7pTNTf7kTS6mp8tn6bv2bLPTF-mktOjdE4nvr62UN_g_ZlV1OrAMAAA:LM=1256376076 +.www.google.de TRUE /search FALSE SS Q0=aGFsbG8 +.www.uzbl.org TRUE /wiki/ FALSE DokuWiki 6eb6jrd6a0a7lljgamrt7b8p97 +.youtube.com TRUE / FALSE GEO 447129ade8c2ab623ae61d089da6b282cwsAAAAzREVUjEKtSuK9rw== +.youtube.com TRUE / FALSE 1569094311 PREF f1=50000000 +.youtube.com TRUE / FALSE 1274470311 VISITOR_INFO1_LIVE eRgRTpP213c +.youtube.com TRUE / FALSE use_hitbox 72c46ff6cbcdb7c5585c36411b6b334edAEAAAAw +.youtube.com TRUE / FALSE watched_video_id_list 9711faf85843bb86dd2c32b6564a5c07WwEAAABzCwAAAF9jMkgzTkhjbkJF diff --git a/.config/uzbl/forms/bbs.archlinux.org b/.config/uzbl/forms/bbs.archlinux.org new file mode 100644 index 0000000..73c1539 --- /dev/null +++ b/.config/uzbl/forms/bbs.archlinux.org @@ -0,0 +1,5 @@ +form_sent: +redirect_url: +req_username: <your username> +req_password: <password> +login: diff --git a/.config/uzbl/history b/.config/uzbl/history new file mode 100644 index 0000000..285f85c --- /dev/null +++ b/.config/uzbl/history @@ -0,0 +1,108 @@ +2009-09-18 10:22:07 uzbl.org +2009-09-18 10:22:19 uzbl.org +2009-09-18 10:22:26 google.de +2009-09-18 10:22:29 http://www.google.de/ Google +2009-09-18 10:22:32 http://images.google.de/imghp?hl=de&tab=wi Google Bilder +2009-09-18 10:23:26 google.de +2009-09-18 10:23:29 http://www.google.de/ Google +2009-09-18 10:23:42 google.de +2009-09-18 10:23:44 http://www.google.de/ Google +2009-09-18 10:23:54 google.de +2009-09-18 10:23:56 http://www.google.de/ Google +2009-09-18 10:24:15 google.de +2009-09-18 10:24:18 http://www.google.de/ Google +2009-09-18 10:24:21 http://www.google.de/preferences?hl=de Einstellungen +2009-09-18 11:01:59 http://google.de +2009-09-18 11:02:01 http://www.google.de/ Google +2009-09-18 11:02:05 http://images.google.de/imghp?hl=de&tab=wi Google Bilder +2009-09-18 11:30:47 http://youtube.com/html5 +2009-09-18 12:16:28 gentoo.orgh +2009-09-18 12:16:35 gentoo.org +2009-09-18 12:17:05 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:12 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:15 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:16 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:16 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:16 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:17 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:17 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:29 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-18 12:17:32 http://www.google.de/ Google +2009-09-18 12:17:36 http://images.google.de/imghp?hl=de&tab=wi Google Bilder +2009-09-18 12:44:12 output +2009-09-18 12:44:12 http://output/ Error +2009-09-18 12:45:26 oggguy.com +2009-09-18 12:45:27 http://oggguy.com/ Error +2009-09-18 12:45:30 ossguy.com +2009-09-18 17:10:33 http://thevideobay.org/play/43/Rage+Against+The+Machine+-+Testify+-+Music+Video/ +2009-09-18 17:10:41 http://thevideobay.org/play/43/Rage+Against+The+Machine+-+Testify+-+Music+Video/ The Video Bay +2009-09-19 17:47:46 google.de +2009-09-19 17:47:49 http://www.google.de/ Google +2009-09-19 17:52:51 google.de +2009-09-19 17:52:54 http://www.google.de/ Google +2009-09-19 17:53:10 http://images.google.de/imghp?hl=de&tab=wi Google Bilder +2009-09-19 17:53:37 ossguy.com +2009-09-19 17:58:30 google.de +2009-09-19 17:58:33 http://www.google.de/ Google +2009-09-19 17:58:42 http://images.google.de/imghp?hl=de&tab=wi Google Bilder +2009-09-19 17:58:45 http://www.google.de/prdhp?hl=de&tab=if Google Produktsuche +2009-09-19 18:17:40 gentoo.org +2009-09-19 18:17:58 http://www.gentoo.org/ Gentoo Linux -- Gentoo Linux News +2009-09-19 18:18:01 http://www.gentoo.org/proj/en/ Gentoo Linux Documentation -- Project Listing +2009-09-19 18:18:06 http://www.gentoo.org/proj/en/council/index.xml Gentoo Linux Projects -- Gentoo Council +2009-09-19 18:29:18 ossguy.com +2009-09-19 18:29:26 ossguy.com +2009-09-19 18:39:37 ossguy.com +2009-09-19 18:42:02 uzbl.org +2009-09-19 20:07:16 google.de +2009-09-19 20:07:19 http://www.google.de/ Google +2009-09-19 20:10:13 uzbl.org +2009-09-19 20:10:25 uzbl.org +2009-09-19 20:10:35 pro-linux.de +2009-09-19 20:10:47 http://www.pro-linux.de/ Pro-Linux +2009-09-19 20:12:17 http://www.pro-linux.de/berichte/ Pro-Linux: Artikel-Index +2009-09-19 20:12:23 http://www.google.de/ Google +2009-09-19 20:14:51 ossguy.com +2009-09-19 22:17:53 uzbl.org +2009-09-19 23:03:17 http://youtube.com/html5 +2009-09-19 23:03:56 uzbl.org +2009-09-19 23:04:04 http://youtube.com/html5 +2009-09-20 00:34:02 ossguy.com +2009-09-21 07:11:22 https://lsf.hs-wismar.de/qisserver/rds?state=qissosreports +2009-09-23 21:31:39 youtube.com +2009-10-24 10:40:48 google.de +2009-10-24 10:40:51 http://www.google.de/ Google +2009-10-24 10:40:56 http://video.google.de/?hl=de&tab=wv Google Videos +2009-10-24 10:41:02 http://maps.google.de/maps?hl=de&tab=vl Google Maps +2009-10-24 10:41:03 http://maps.google.de/maps?hl=de&tab=vl Google Maps +2009-10-24 10:41:04 http://www.google.de/products?hl=de&sa=N&tab=lf Google Produktsuche +2009-10-24 10:41:07 http://images.google.de/imghp?hl=de&tab=fi Google Bilder +2009-10-24 10:41:09 http://www.google.de/webhp?hl=de&tab=iw Google +2009-10-24 10:41:25 http://www.youtube.com/ YouTube - Broadcast Yourself. +2009-10-24 10:41:30 http://www.youtube.com/ YouTube - Broadcast Yourself. +2009-10-24 10:41:31 http://www.youtube.com/results?search_query=&search_type=&aq=f YouTube +2009-10-24 10:41:54 http://www.youtube.com/results?search_query=beyonce&search_type=&aq=f YouTube - beyonce +2009-10-24 10:41:55 http://www.youtube.com/results?search_query=beyonce&search_type=&aq=f YouTube - beyonce +2009-10-24 10:42:47 http://www.youtube.com/user/beyonce?blend=1&ob=4 YouTube - Kanal von beyonce +2009-10-24 10:43:18 http://www.google.de/ Google +2009-10-24 10:43:29 http://www.google.de/search?hl=de&source=hp&ie=ISO-8859-1&q=hallo&btnG=Google-Suche&meta=&aq=f&oq= hallo - Google-Suche +2009-10-24 10:51:43 youtube.com +2009-10-24 10:51:51 http://www.youtube.com/ YouTube - Broadcast Yourself. +2009-10-24 10:51:51 http://www.youtube.com/ YouTube - Broadcast Yourself. +2009-10-24 10:51:57 http://www.youtube.com/results?search_query=TI+Semto&search_type= YouTube - TI Semto +2009-10-24 10:51:57 http://www.youtube.com/results?search_query=TI+Semto&search_type= YouTube - TI Semto +2009-10-24 10:52:04 http://www.youtube.com/watch?v=_c2H3NHcnBE YouTube - Scooter- Ti Sento (Official Video HQ) + Lyrics +2009-10-24 10:52:04 http://www.youtube.com/watch?v=_c2H3NHcnBE YouTube - Scooter- Ti Sento (Official Video HQ) + Lyrics +2009-10-24 10:52:29 http://www.youtube.com/watch?v=_c2H3NHcnBE YouTube - Scooter- Ti Sento (Official Video HQ) + Lyrics +2009-10-24 11:20:41 http://www.uzbl.org/ Uzbl - a browser that adheres to the unix philosophy. +2009-10-24 11:20:50 http://www.google.de/ Google +2009-10-24 11:21:02 http://images.google.de/imghp?hl=de&tab=wi Google Bilder +2009-10-24 11:21:02 http://www.google.de/webhp?hl=de&tab=iw Google +2009-10-24 11:21:24 http://www.google.de/ig?hl=de&tab=iw&source=iglk iGoogle +2009-10-24 11:21:49 http://www.google.de/ig?hl=de&tab=iw&source=iglk iGoogle +2009-10-24 11:21:49 http://www.google.de/ig?hl=de&tab=iw&source=iglk iGoogle +2009-10-24 11:21:49 http://www.google.de/ig?hl=de&tab=iw&source=iglk iGoogle +2009-11-29 13:09:40 uzbl.org +2009-11-29 13:09:47 http://www.uzbl.org/ Uzbl - web interface tools which adhere to the unix philosophy. +2009-11-29 13:10:07 http://www.google.de/ig?hl=de iGoogle +2009-11-29 13:10:09 http://www.google.de/ig?hl=de iGoogle diff --git a/.config/uzbl/scripts/clipboard.sh b/.config/uzbl/scripts/clipboard.sh new file mode 100755 index 0000000..60567d3 --- /dev/null +++ b/.config/uzbl/scripts/clipboard.sh @@ -0,0 +1,17 @@ +#!/bin/sh + +# with this script you can store the current url in the clipboard, or go to the url which is stored in the clipboard. + +clip=xclip + +fifo="$5" +action="$1" +url="$7" + +selection=`$clip -o` + +case $action in + "yank" ) echo -n "$url" | eval "$clip";; + "goto" ) echo "uri $selection" > "$fifo";; + * ) echo "clipboard.sh: invalid action";; +esac diff --git a/.config/uzbl/scripts/cookie_daemon.py b/.config/uzbl/scripts/cookie_daemon.py new file mode 100755 index 0000000..87a2e87 --- /dev/null +++ b/.config/uzbl/scripts/cookie_daemon.py @@ -0,0 +1,662 @@ +#!/usr/bin/env python + +# The Python Cookie Daemon for Uzbl. +# Copyright (c) 2009, Tom Adams <tom@holizz.com> +# Copyright (c) 2009, Dieter Plaetinck <dieter@plaetinck.be> +# Copyright (c) 2009, Mason Larobina <mason.larobina@gmail.com> +# Copyright (c) 2009, Michael Fiano <axionix@gmail.com> +# +# 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/>. + +''' +The Python Cookie Daemon +======================== + +This daemon is a re-write of the original cookies.py script found in uzbl's +master branch. This script provides more functionality than the original +cookies.py by adding numerous command line options to specify different cookie +jar locations, socket locations, verbose output, etc. This functionality is +very useful as it allows you to run multiple daemons at once serving cookies +to different groups of uzbl instances as required. + +Keeping up to date +================== + +Check the cookie daemon uzbl-wiki page for more information on where to +find the latest version of the cookie_daemon.py + + http://www.uzbl.org/wiki/cookie_daemon.py + +Command line options +==================== + +Use the following command to get a full list of the cookie_daemon.py command +line options: + + ./cookie_daemon.py --help + +Talking with uzbl +================= + +In order to get uzbl to talk to a running cookie daemon you add the following +to your uzbl config: + + set cookie_handler = talk_to_socket $XDG_CACHE_HOME/uzbl/cookie_daemon_socket + +Or if you prefer using the $HOME variable: + + set cookie_handler = talk_to_socket $HOME/.cache/uzbl/cookie_daemon_socket + +Todo list +========= + + - Use a pid file to make force killing a running daemon possible. + +Reporting bugs / getting help +============================= + +The best way to report bugs and or get help with the cookie daemon is to +contact the maintainers it the #uzbl irc channel found on the Freenode IRC +network (irc.freenode.org). +''' + +import cookielib +import os +import sys +import urllib2 +import select +import socket +import time +import atexit +from traceback import print_exc +from signal import signal, SIGTERM +from optparse import OptionParser +from os.path import join + +try: + import cStringIO as StringIO + +except ImportError: + import StringIO + + +# ============================================================================ +# ::: Default configuration section :::::::::::::::::::::::::::::::::::::::::: +# ============================================================================ + +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 join(os.environ['HOME'], default) + +# Setup xdg paths. +CACHE_DIR = join(xdghome('CACHE', '.cache/'), 'uzbl/') +DATA_DIR = join(xdghome('DATA', '.local/share/'), 'uzbl/') +CONFIG_DIR = join(xdghome('CONFIG', '.config/'), 'uzbl/') + +# Ensure data paths exist. +for path in [CACHE_DIR, DATA_DIR, CONFIG_DIR]: + if not os.path.exists(path): + os.makedirs(path) + +# Default config +config = { + + # Default cookie jar, whitelist, and daemon socket locations. + 'cookie_jar': join(DATA_DIR, 'cookies.txt'), + 'cookie_whitelist': join(CONFIG_DIR, 'cookie_whitelist'), + 'cookie_socket': join(CACHE_DIR, 'cookie_daemon_socket'), + + # Don't use a cookie whitelist policy by default. + 'use_whitelist': False, + + # Time out after x seconds of inactivity (set to 0 for never time out). + # WARNING: Do not use this option if you are manually launching the daemon. + 'daemon_timeout': 0, + + # Daemonise by default. + 'daemon_mode': True, + + # Optionally print helpful debugging messages to the terminal. + 'verbose': False, + +} # End of config dictionary. + + +# ============================================================================ +# ::: End of configuration section ::::::::::::::::::::::::::::::::::::::::::: +# ============================================================================ + + +_SCRIPTNAME = os.path.basename(sys.argv[0]) +def echo(msg): + '''Prints only if the verbose flag has been set.''' + + if config['verbose']: + sys.stderr.write("%s: %s\n" % (_SCRIPTNAME, msg)) + + +def error(msg): + '''Prints error message and exits.''' + + sys.stderr.write("%s: error: %s\n" % (_SCRIPTNAME, msg)) + sys.exit(1) + + +def mkbasedir(filepath): + '''Create the base directories of the file in the file-path if the dirs + don't exist.''' + + dirname = os.path.dirname(filepath) + if not os.path.exists(dirname): + echo("creating dirs: %r" % dirname) + os.makedirs(dirname) + + +def daemon_running(cookie_socket): + '''Check if another process (hopefully a cookie_daemon.py) is listening + on the cookie daemon socket. If another process is found to be + listening on the socket exit the daemon immediately and leave the + socket alone. If the connect fails assume the socket has been abandoned + and delete it (to be re-created in the create socket function).''' + + if not os.path.exists(cookie_socket): + return False + + if os.path.isfile(cookie_socket): + raise Exception("regular file at %r is not a socket" % cookie_socket) + + + if os.path.isdir(cookie_socket): + raise Exception("directory at %r is not a socket" % cookie_socket) + + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + sock.connect(cookie_socket) + sock.close() + echo("detected daemon listening on %r" % cookie_socket) + return True + + except socket.error: + # Failed to connect to cookie_socket so assume it has been + # abandoned by another cookie daemon process. + if os.path.exists(cookie_socket): + echo("deleting abandoned socket at %r" % cookie_socket) + os.remove(cookie_socket) + + return False + + +def send_command(cookie_socket, cmd): + '''Send a command to a running cookie daemon.''' + + if not daemon_running(cookie_socket): + return False + + try: + sock = socket.socket(socket.AF_UNIX, socket.SOCK_SEQPACKET) + sock.connect(cookie_socket) + sock.send(cmd) + sock.close() + echo("sent command %r to %r" % (cmd, cookie_socket)) + return True + + except socket.error: + print_exc() + error("failed to send message %r to %r" % (cmd, cookie_socket)) + return False + + +def kill_daemon(cookie_socket): + '''Send the "EXIT" command to running cookie_daemon.''' + + if send_command(cookie_socket, "EXIT"): + # Now ensure the cookie_socket is cleaned up. + start = time.time() + while os.path.exists(cookie_socket): + time.sleep(0.1) + if (time.time() - start) > 5: + error("force deleting socket %r" % cookie_socket) + os.remove(cookie_socket) + return + + echo("stopped daemon listening on %r"% cookie_socket) + + else: + if os.path.exists(cookie_socket): + os.remove(cookie_socket) + echo("removed abandoned/broken socket %r" % cookie_socket) + + +def daemonize(): + '''Daemonize the process using the Stevens' double-fork magic.''' + + try: + if os.fork(): + os._exit(0) + + except OSError: + print_exc() + sys.stderr.write("fork #1 failed") + sys.exit(1) + + os.chdir('/') + os.setsid() + os.umask(0) + + try: + if os.fork(): + os._exit(0) + + except OSError: + print_exc() + sys.stderr.write("fork #2 failed") + sys.exit(1) + + 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()) + + +class CookieMonster: + '''The uzbl cookie daemon class.''' + + def __init__(self): + '''Initialise class variables.''' + + self.server_socket = None + self.jar = None + self.last_request = time.time() + self._running = False + + + def run(self): + '''Start the daemon.''' + + # The check healthy function will exit if another daemon is detected + # listening on the cookie socket and remove the abandoned socket if + # there isnt. + if os.path.exists(config['cookie_socket']): + if daemon_running(config['cookie_socket']): + sys.exit(1) + + # Daemonize process. + if config['daemon_mode']: + echo("entering daemon mode") + daemonize() + + # Register a function to cleanup on exit. + atexit.register(self.quit) + + # Make SIGTERM act orderly. + signal(SIGTERM, lambda signum, stack_frame: sys.exit(1)) + + # Create cookie jar object from file. + self.open_cookie_jar() + + # Create a way to exit nested loops by setting a running flag. + self._running = True + + while self._running: + # Create cookie daemon socket. + self.create_socket() + + try: + # Enter main listen loop. + self.listen() + + except KeyboardInterrupt: + self._running = False + print + + except socket.error: + print_exc() + + except: + # Clean up + self.del_socket() + + # Raise exception + raise + + # Always delete the socket before calling create again. + self.del_socket() + + + def load_whitelist(self): + '''Load the cookie jar whitelist policy.''' + + cookie_whitelist = config['cookie_whitelist'] + + if cookie_whitelist: + mkbasedir(cookie_whitelist) + + # Create cookie whitelist file if it does not exist. + if not os.path.exists(cookie_whitelist): + open(cookie_whitelist, 'w').close() + + # Read cookie whitelist file into list. + file = open(cookie_whitelist,'r') + domain_list = [line.rstrip('\n') for line in file] + file.close() + + # Define policy of allowed domains + policy = cookielib.DefaultCookiePolicy(allowed_domains=domain_list) + self.jar.set_policy(policy) + + # Save the last modified time of the whitelist. + self._whitelistmtime = os.stat(cookie_whitelist).st_mtime + + + def open_cookie_jar(self): + '''Open the cookie jar.''' + + cookie_jar = config['cookie_jar'] + cookie_whitelist = config['cookie_whitelist'] + + if cookie_jar: + mkbasedir(cookie_jar) + + # Create cookie jar object from file. + self.jar = cookielib.MozillaCookieJar(cookie_jar) + + # Load cookie whitelist policy. + if config['use_whitelist']: + self.load_whitelist() + + if cookie_jar: + try: + # Attempt to load cookies from the cookie jar. + self.jar.load(ignore_discard=True) + + # Ensure restrictive permissions are set on the cookie jar + # to prevent other users on the system from hi-jacking your + # authenticated sessions simply by copying your cookie jar. + os.chmod(cookie_jar, 0600) + + except: + pass + + + def reload_whitelist(self): + '''Reload the cookie whitelist.''' + + cookie_whitelist = config['cookie_whitelist'] + if os.path.exists(cookie_whitelist): + echo("reloading whitelist %r" % cookie_whitelist) + self.open_cookie_jar() + + + def create_socket(self): + '''Create AF_UNIX socket for communication with uzbl instances.''' + + cookie_socket = config['cookie_socket'] + mkbasedir(cookie_socket) + + self.server_socket = socket.socket(socket.AF_UNIX, + socket.SOCK_SEQPACKET) + + self.server_socket.bind(cookie_socket) + + # Set restrictive permissions on the cookie socket to prevent other + # users on the system from data-mining your cookies. + os.chmod(cookie_socket, 0600) + + + def listen(self): + '''Listen for incoming cookie PUT and GET requests.''' + + daemon_timeout = config['daemon_timeout'] + echo("listening on %r" % config['cookie_socket']) + + while self._running: + # This line tells the socket how many pending incoming connections + # to enqueue at once. Raising this number may or may not increase + # performance. + self.server_socket.listen(1) + + if bool(select.select([self.server_socket], [], [], 1)[0]): + client_socket, _ = self.server_socket.accept() + self.handle_request(client_socket) + self.last_request = time.time() + client_socket.close() + continue + + if daemon_timeout: + # Checks if the daemon has been idling for too long. + idle = time.time() - self.last_request + if idle > daemon_timeout: + self._running = False + + + def handle_request(self, client_socket): + '''Connection made, now to serve a cookie PUT or GET request.''' + + # Receive cookie request from client. + data = client_socket.recv(8192) + if not data: + return + + # Cookie argument list in packet is null separated. + argv = data.split("\0") + action = argv[0].upper().strip() + + # Catch the EXIT command sent to kill running daemons. + if action == "EXIT": + self._running = False + return + + # Catch whitelist RELOAD command. + elif action == "RELOAD": + self.reload_whitelist() + return + + # Return if command unknown. + elif action not in ['GET', 'PUT']: + error("unknown command %r." % argv) + return + + # Determine whether or not to print cookie data to terminal. + print_cookie = (config['verbose'] and not config['daemon_mode']) + if print_cookie: + print ' '.join(argv[:4]) + + uri = urllib2.urlparse.ParseResult( + scheme=argv[1], + netloc=argv[2], + path=argv[3], + params='', + query='', + fragment='').geturl() + + req = urllib2.Request(uri) + + if action == "GET": + self.jar.add_cookie_header(req) + if req.has_header('Cookie'): + cookie = req.get_header('Cookie') + client_socket.send(cookie) + if print_cookie: + print cookie + + else: + client_socket.send("\0") + + elif action == "PUT": + cookie = argv[4] if len(argv) > 3 else None + if print_cookie: + print cookie + + self.put_cookie(req, cookie) + + if print_cookie: + print + + + def put_cookie(self, req, cookie=None): + '''Put a cookie in the cookie jar.''' + + hdr = urllib2.httplib.HTTPMessage(\ + StringIO.StringIO('Set-Cookie: %s' % cookie)) + res = urllib2.addinfourl(StringIO.StringIO(), hdr, + req.get_full_url()) + self.jar.extract_cookies(res, req) + if config['cookie_jar']: + self.jar.save(ignore_discard=True) + + + def del_socket(self): + '''Remove the cookie_socket file on exit. In a way the cookie_socket + is the daemons pid file equivalent.''' + + if self.server_socket: + try: + self.server_socket.close() + + except: + pass + + self.server_socket = None + + cookie_socket = config['cookie_socket'] + if os.path.exists(cookie_socket): + echo("deleting socket %r" % cookie_socket) + os.remove(cookie_socket) + + + def quit(self): + '''Called on exit to make sure all loose ends are tied up.''' + + self.del_socket() + sys.exit(0) + + +def main(): + '''Main function.''' + + # Define command line parameters. + usage = "usage: %prog [options] {start|stop|restart|reload}" + parser = OptionParser(usage=usage) + parser.add_option('-n', '--no-daemon', dest='no_daemon', + action='store_true', help="don't daemonise the process.") + + parser.add_option('-v', '--verbose', dest="verbose", + action='store_true', help="print verbose output.") + + parser.add_option('-t', '--daemon-timeout', dest='daemon_timeout', + action="store", metavar="SECONDS", help="shutdown the daemon after x "\ + "seconds inactivity. WARNING: Do not use this when launching the "\ + "cookie daemon manually.") + + parser.add_option('-s', '--cookie-socket', dest="cookie_socket", + metavar="SOCKET", help="manually specify the socket location.") + + parser.add_option('-j', '--cookie-jar', dest='cookie_jar', + metavar="FILE", help="manually specify the cookie jar location.") + + parser.add_option('-m', '--memory', dest='memory', action='store_true', + help="store cookies in memory only - do not write to disk") + + parser.add_option('-u', '--use-whitelist', dest='usewhitelist', + action='store_true', help="use cookie whitelist policy") + + parser.add_option('-w', '--cookie-whitelist', dest='whitelist', + action='store', help="manually specify whitelist location", + metavar='FILE') + + # Parse the command line arguments. + (options, args) = parser.parse_args() + + expand = lambda p: os.path.realpath(os.path.expandvars(p)) + + initcommands = ['start', 'stop', 'restart', 'reload'] + for arg in args: + if arg not in initcommands: + error("unknown argument %r" % args[0]) + sys.exit(1) + + if len(args) > 1: + error("the daemon only accepts one {%s} action at a time." + % '|'.join(initcommands)) + sys.exit(1) + + if len(args): + action = args[0] + + else: + action = "start" + + if options.no_daemon: + config['daemon_mode'] = False + + if options.cookie_socket: + config['cookie_socket'] = expand(options.cookie_socket) + + if options.cookie_jar: + config['cookie_jar'] = expand(options.cookie_jar) + + if options.memory: + config['cookie_jar'] = None + + if options.whitelist: + config['cookie_whitelist'] = expand(options.whitelist) + + if options.whitelist or options.usewhitelist: + config['use_whitelist'] = True + + if options.daemon_timeout: + try: + config['daemon_timeout'] = int(options.daemon_timeout) + + except ValueError: + error("expected int argument for -t, --daemon-timeout") + + # Expand $VAR's in config keys that relate to paths. + for key in ['cookie_socket', 'cookie_jar', 'cookie_whitelist']: + if config[key]: + config[key] = os.path.expandvars(config[key]) + + if options.verbose: + config['verbose'] = True + import pprint + sys.stderr.write("%s\n" % pprint.pformat(config)) + + # It would be better if we didn't need to start this python process just + # to send a command to the socket, but unfortunately socat doesn't seem + # to support SEQPACKET. + if action == "reload": + send_command(config['cookie_socket'], "RELOAD") + + if action in ['stop', 'restart']: + kill_daemon(config['cookie_socket']) + + if action in ['start', 'restart']: + CookieMonster().run() + + +if __name__ == "__main__": + main() diff --git a/.config/uzbl/scripts/cookies.py b/.config/uzbl/scripts/cookies.py new file mode 100755 index 0000000..10f90fa --- /dev/null +++ b/.config/uzbl/scripts/cookies.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python + +import StringIO, cookielib, os, sys, urllib2 + +if __name__ == '__main__': + action = sys.argv[8] + uri = urllib2.urlparse.ParseResult( + scheme=sys.argv[9], + netloc=sys.argv[10], + path=sys.argv[11], + params='', + query='', + fragment='').geturl() + set_cookie = sys.argv[12] if len(sys.argv)>12 else None + + if 'XDG_DATA_HOME' in os.environ.keys() and os.environ['XDG_DATA_HOME']: + f = os.path.join(os.environ['XDG_DATA_HOME'],'uzbl/cookies.txt') + else: + f = os.path.join(os.environ['HOME'],'.local/share/uzbl/cookies.txt') + jar = cookielib.MozillaCookieJar(f) + + try: + jar.load(ignore_discard=True) + except: + pass + + req = urllib2.Request(uri) + + if action == 'GET': + jar.add_cookie_header(req) + if req.has_header('Cookie'): + print req.get_header('Cookie') + elif action == 'PUT': + hdr = urllib2.httplib.HTTPMessage(StringIO.StringIO('Set-Cookie: %s' % set_cookie)) + res = urllib2.addinfourl(StringIO.StringIO(), hdr, req.get_full_url()) + jar.extract_cookies(res,req) + jar.save(ignore_discard=True) diff --git a/.config/uzbl/scripts/cookies.sh b/.config/uzbl/scripts/cookies.sh new file mode 100755 index 0000000..339c6fc --- /dev/null +++ b/.config/uzbl/scripts/cookies.sh @@ -0,0 +1,154 @@ +#!/bin/sh + +set -n; + +# THIS IS EXPERIMENTAL AND COULD BE INSECURE !!!!!! + +# this is an example bash script of how you could manage your cookies. it is very raw and basic and not as good as cookies.py +# we use the cookies.txt format (See http://kb.mozillazine.org/Cookies.txt) +# This is one textfile with entries like this: +# kb.mozillazine.org FALSE / FALSE 1146030396 wikiUserID 16993 +# domain alow-read-other-subdomains path http-required expiration name value +# you probably want your cookies config file in your $XDG_CONFIG_HOME ( eg $HOME/.config/uzbl/cookies) +# Note. in uzbl there is no strict definition on what a session is. it's YOUR job to clear cookies marked as end_session if you want to keep cookies only valid during a "session" +# MAYBE TODO: allow user to edit cookie before saving. this cannot be done with zenity :( +# TODO: different cookie paths per config (eg per group of uzbl instances) + +# TODO: correct implementation. +# see http://curl.haxx.se/rfc/cookie_spec.html +# http://en.wikipedia.org/wiki/HTTP_cookie + +# TODO : check expires= before sending. +# write sample script that cleans up cookies dir based on expires attribute. +# TODO: check uri against domain attribute. and path also. +# implement secure attribute. +# support blocking or not for 3rd parties +# http://kb.mozillazine.org/Cookies.txt +# don't always append cookies, sometimes we need to overwrite + +cookie_config=${XDG_CONFIG_HOME:-${HOME}/.config}/uzbl/cookies +[ "x$cookie_config" = x ] && exit 1 +[ -d "${XDG_DATA_HOME:-${HOME}/.local/share}/uzbl/" ] &&\ +cookie_data=${XDG_DATA_HOME:-${HOME}/.local/share}/uzbl/cookies.txt || exit 1 + +notifier= +#notifier=notify-send +#notify_wrapper () { +# echo "$@" >> $HOME/cookielog +#} +#notifier=notifier_wrapper + +# if this variable is set, we will use it to inform you when and which cookies we store, and when/which we send. +# it's primarily used for debugging +notifier= +which zenity &>/dev/null || exit 2 + +# Example cookie: +# test_cookie=CheckForPermission; expires=Thu, 07-May-2009 19:17:55 GMT; path=/; domain=.doubleclick.net + +# uri=$6 +# uri=${uri/http:\/\/} # strip 'http://' part +# host=${uri/\/*/} +action=$8 # GET/PUT +shift +host=$9 +shift +path=$9 +shift +cookie=$9 + +field_domain=$host +field_path=$path +field_name= +field_value= +field_exp='end_session' + +notify() { + [ -n "$notifier" ] && $notifier "$@" +} + + +# FOR NOW LETS KEEP IT SIMPLE AND JUST ALWAYS PUT AND ALWAYS GET +parse_cookie() { + IFS=$';' + first_pair=1 + for pair in $cookie + do + if [ "x$first_pair" = x1 ] + then + field_name=${pair%%=*} + field_value=${pair#*=} + first_pair=0 + else + echo "$pair" | read -r pair #strip leading/trailing wite space + key=${pair%%=*} + val=${pair#*=} + [ "$key" == expires ] && field_exp=`date -u -d "$val" +'%s'` + # TODO: domain + [ "$key" == path ] && field_path=$val + fi + done + unset IFS +} + +# match cookies in cookies.txt against hostname and path +get_cookie() { + path_esc=${path//\//\\/} + search="^[^\t]*$host\t[^\t]*\t$path_esc" + cookie=`awk "/$search/" $cookie_data 2>/dev/null | tail -n 1` + if [ -z "$cookie" ] + then + notify "Get_cookie: search: $search in $cookie_data -> no result" + false + else + notify "Get_cookie: search: $search in $cookie_data -> result: $cookie" + echo "$cookie" | \ + read domain alow_read_other_subdomains path http_required expiration name \ + value; + cookie="$name=$value" + true + fi +} + +save_cookie() { + if parse_cookie + then + data="$field_domain\tFALSE\t$field_path\tFALSE\t$field_exp\t$field_name\t$field_value" + notify "save_cookie: adding $data to $cookie_data" + echo -e "$data" >> $cookie_data + else + notify "not saving a cookie. since we don't have policies yet, parse_cookie must have returned false. this is a bug" + fi +} + +[ "x$action" = xPUT ] && save_cookie +[ "x$action" = xGET ] && get_cookie && echo "$cookie" + +exit + + +# TODO: implement this later. +# $1 = section (TRUSTED or DENY) +# $2 =url +match() { + sed -n "/$1/,/^\$/p" $cookie_config 2>/dev/null | grep -q "^$host" +} + +fetch_cookie() { + cookie=`cat $cookie_data` +} + +store_cookie() { + echo $cookie > $cookie_data +} + +if match TRUSTED $host +then + [ "x$action" = xPUT ] && store_cookie $host + [ "x$action" = xGET ] && fetch_cookie && echo "$cookie" +elif ! match DENY $host +then + [ "x$action" = xPUT ] && cookie=`zenity --entry --title 'Uzbl Cookie handler' --text "Accept this cookie from $host ?" --entry-text="$cookie"` && store_cookie $host + [ "x$action" = xGET ] && fetch_cookie && cookie=`zenity --entry --title 'Uzbl Cookie handler' --text "Submit this cookie to $host ?" --entry-text="$cookie"` && echo $cookie +fi +exit 0 diff --git a/.config/uzbl/scripts/download.sh b/.config/uzbl/scripts/download.sh new file mode 100755 index 0000000..c8eb6ba --- /dev/null +++ b/.config/uzbl/scripts/download.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# just an example of how you could handle your downloads +# try some pattern matching on the uri to determine what we should do + +# Some sites block the default wget --user-agent.. +GET="wget --user-agent=Firefox" + +dest="$HOME" +url="$8" + +http_proxy="$9" +export http_proxy + +test "x$url" = "x" && { echo "you must supply a url! ($url)"; exit 1; } + +# only changes the dir for the $get sub process +if echo "$url" | grep -E '.*\.torrent' >/dev/null; +then + ( cd "$dest"; eval "$GET" "$url") +else + ( cd "$dest"; eval "$GET" "$url") +fi diff --git a/.config/uzbl/scripts/follow_Numbers.js b/.config/uzbl/scripts/follow_Numbers.js new file mode 100644 index 0000000..efde4d7 --- /dev/null +++ b/.config/uzbl/scripts/follow_Numbers.js @@ -0,0 +1,223 @@ +/* This is the basic linkfollowing script. + * Its pretty stable, only using numbers to navigate. + * + * TODO: Some pages mess around a lot with the zIndex which + * lets some hints in the background. + * TODO: Some positions are not calculated correctly (mostly + * because of uber-fancy-designed-webpages. Basic HTML and CSS + * works good + * TODO: Still some links can't be followed/unexpected things + * happen. Blame some freaky webdesigners ;) + */ + +//Just some shortcuts and globals +var uzblid = 'uzbl_link_hint'; +var uzbldivid = uzblid + '_div_container'; +var doc = document; +var win = window; +var links = document.links; +var forms = document.forms; +//Make onlick-links "clickable" +try { + HTMLElement.prototype.click = function() { + if (typeof this.onclick == 'function') { + this.onclick({ + type: 'click' + }); + } + }; +} catch(e) {} +//Catch the ESC keypress to stop linkfollowing +function keyPressHandler(e) { + var kC = window.event ? event.keyCode: e.keyCode; + var Esc = window.event ? 27 : e.DOM_VK_ESCAPE; + if (kC == Esc) { + removeAllHints(); + } +} +//Calculate element position to draw the hint +//Pretty accurate but on fails in some very fancy cases +function elementPosition(el) { + var up = el.offsetTop; + var left = el.offsetLeft; + var width = el.offsetWidth; + var height = el.offsetHeight; + while (el.offsetParent) { + el = el.offsetParent; + up += el.offsetTop; + left += el.offsetLeft; + } + return [up, left, width, height]; +} +//Calculate if an element is visible +function isVisible(el) { + if (el == doc) { + return true; + } + if (!el) { + return false; + } + if (!el.parentNode) { + return false; + } + if (el.style) { + if (el.style.display == 'none') { + return false; + } + if (el.style.visibility == 'hidden') { + return false; + } + } + return isVisible(el.parentNode); +} +//Calculate if an element is on the viewport. +function elementInViewport(el) { + offset = elementPosition(el); + var up = offset[0]; + var left = offset[1]; + var width = offset[2]; + var height = offset[3]; + return up < window.pageYOffset + window.innerHeight && left < window.pageXOffset + window.innerWidth && (up + height) > window.pageYOffset && (left + width) > window.pageXOffset; +} +//Removes all hints/leftovers that might be generated +//by this script. +function removeAllHints() { + var elements = doc.getElementById(uzbldivid); + if (elements) { + elements.parentNode.removeChild(elements); + } +} +//Generate a hint for an element with the given label +//Here you can play around with the style of the hints! +function generateHint(el, label) { + var pos = elementPosition(el); + var hint = doc.createElement('div'); + hint.setAttribute('name', uzblid); + hint.innerText = label; + hint.style.display = 'inline'; + hint.style.backgroundColor = '#B9FF00'; + hint.style.border = '2px solid #4A6600'; + hint.style.color = 'black'; + hint.style.fontSize = '9px'; + hint.style.fontWeight = 'bold'; + hint.style.lineHeight = '9px'; + hint.style.margin = '0px'; + hint.style.padding = '1px'; + hint.style.position = 'absolute'; + hint.style.zIndex = '1000'; + hint.style.left = pos[1] + 'px'; + hint.style.top = pos[0] + 'px'; + var img = el.getElementsByTagName('img'); + if (img.length > 0) { + hint.style.left = pos[1] + img[0].width / 2 + 'px'; + } + hint.style.textDecoration = 'none'; + hint.style.webkitBorderRadius = '6px'; + // Play around with this, pretty funny things to do :) + hint.style.webkitTransform = 'scale(1) rotate(0deg) translate(-6px,-5px)'; + return hint; +} +//Here we choose what to do with an element if we +//want to "follow" it. On form elements we "select" +//or pass the focus, on links we try to perform a click, +//but at least set the href of the link. (needs some improvements) +function clickElem(item) { + removeAllHints(); + if (item) { + var name = item.tagName; + if (name == 'A') { + item.click(); + window.location = item.href; + } else if (name == 'INPUT') { + var type = item.getAttribute('type').toUpperCase(); + if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') { + item.focus(); + item.select(); + } else { + item.click(); + } + } else if (name == 'TEXTAREA' || name == 'SELECT') { + item.focus(); + item.select(); + } else { + item.click(); + window.location = item.href; + } + } +} +//Returns a list of all links (in this version +//just the elements itself, but in other versions, we +//add the label here. +function addLinks() { + res = [[], []]; + for (var l = 0; l < links.length; l++) { + var li = links[l]; + if (isVisible(li) && elementInViewport(li)) { + res[0].push(li); + } + } + return res; +} +//Same as above, just for the form elements +function addFormElems() { + res = [[], []]; + for (var f = 0; f < forms.length; f++) { + for (var e = 0; e < forms[f].elements.length; e++) { + var el = forms[f].elements[e]; + if (el && ['INPUT', 'TEXTAREA', 'SELECT'].indexOf(el.tagName) + 1 && isVisible(el) && elementInViewport(el)) { + res[0].push(el); + } + } + } + return res; +} +//Draw all hints for all elements passed. "len" is for +//the number of chars we should use to avoid collisions +function reDrawHints(elems, chars) { + removeAllHints(); + var hintdiv = doc.createElement('div'); + hintdiv.setAttribute('id', uzbldivid); + for (var i = 0; i < elems[0].length; i++) { + if (elems[0][i]) { + var label = elems[1][i].substring(chars); + var h = generateHint(elems[0][i], label); + hintdiv.appendChild(h); + } + } + if (document.body) { + document.body.appendChild(hintdiv); + } +} +//Put it all together +function followLinks(follow) { + var s = follow.split(''); + var linknr = parseInt(follow, 10); + if (document.body) document.body.setAttribute('onkeyup', 'keyPressHandler(event)'); + var linkelems = addLinks(); + var formelems = addFormElems(); + var elems = [linkelems[0].concat(formelems[0]), linkelems[1].concat(formelems[1])]; + var len = (elems[0].length + '').length; + var oldDiv = doc.getElementById(uzbldivid); + var leftover = [[], []]; + if (linknr + 1 && s.length == len && linknr < elems[0].length && linknr >= 0) { + clickElem(elems[0][linknr]); + } else { + for (var j = 0; j < elems[0].length; j++) { + var b = true; + var label = j + ''; + var n = label.length; + for (n; n < len; n++) { + label = '0' + label; + } + for (var k = 0; k < s.length; k++) { + b = b && label.charAt(k) == s[k]; + } + if (b) { + leftover[0].push(elems[0][j]); + leftover[1].push(label); + } + } + reDrawHints(leftover, s.length); + } +} +followLinks('%s'); diff --git a/.config/uzbl/scripts/follow_Numbers_Strings.js b/.config/uzbl/scripts/follow_Numbers_Strings.js new file mode 100644 index 0000000..67da2f9 --- /dev/null +++ b/.config/uzbl/scripts/follow_Numbers_Strings.js @@ -0,0 +1,205 @@ +var uzblid = 'uzbl_link_hint'; +var uzbldivid = uzblid + '_div_container'; +var doc = document; +var win = window; +var links = document.links; +var forms = document.forms; +try { + HTMLElement.prototype.click = function() { + if (typeof this.onclick == 'function') { + this.onclick({ + type: 'click' + }); + } + }; +} catch(e) {} +function keyPressHandler(e) { + var kC = window.event ? event.keyCode: e.keyCode; + var Esc = window.event ? 27 : e.DOM_VK_ESCAPE; + if (kC == Esc) { + removeAllHints(); + } +} +function elementPosition(el) { + var up = el.offsetTop; + var left = el.offsetLeft; + var width = el.offsetWidth; + var height = el.offsetHeight; + while (el.offsetParent) { + el = el.offsetParent; + up += el.offsetTop; + left += el.offsetLeft; + } + return [up, left, width, height]; +} +function isVisible(el) { + if (el == doc) { + return true; + } + if (!el) { + return false; + } + if (!el.parentNode) { + return false; + } + if (el.style) { + if (el.style.display == 'none') { + return false; + } + if (el.style.visibility == 'hidden') { + return false; + } + } + return isVisible(el.parentNode); +} +function elementInViewport(el) { + offset = elementPosition(el); + var up = offset[0]; + var left = offset[1]; + var width = offset[2]; + var height = offset[3]; + return up < window.pageYOffset + window.innerHeight && left < window.pageXOffset + window.innerWidth && (up + height) > window.pageYOffset && (left + width) > window.pageXOffset; +} +function removeAllHints() { + var elements = doc.getElementById(uzbldivid); + if (elements) { + elements.parentNode.removeChild(elements); + } +} +function generateHint(el, label) { + var pos = elementPosition(el); + var hint = doc.createElement('div'); + hint.setAttribute('name', uzblid); + hint.innerText = label; + hint.style.display = 'inline'; + hint.style.backgroundColor = '#B9FF00'; + hint.style.border = '2px solid #4A6600'; + hint.style.color = 'black'; + hint.style.zIndex = '1000'; + hint.style.fontSize = '9px'; + hint.style.fontWeight = 'bold'; + hint.style.lineHeight = '9px'; + hint.style.margin = '0px'; + hint.style.padding = '1px'; + hint.style.position = 'absolute'; + hint.style.left = pos[1] + 'px'; + hint.style.top = pos[0] + 'px'; + var img = el.getElementsByTagName('img'); + if (img.length > 0) { + hint.style.left = pos[1] + img[0].width / 2 + 'px'; + } + hint.style.textDecoration = 'none'; + hint.style.webkitBorderRadius = '6px'; + hint.style.webkitTransform = 'scale(1) rotate(0deg) translate(-6px,-5px)'; + return hint; +} +function clickElem(item) { + removeAllHints(); + if (item) { + var name = item.tagName; + if (name == 'A') { + item.click(); + window.location = item.href; + } else if (name == 'INPUT') { + var type = item.getAttribute('type').toUpperCase(); + if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') { + item.focus(); + item.select(); + } else { + item.click(); + } + } else if (name == 'TEXTAREA' || name == 'SELECT') { + item.focus(); + item.select(); + } else { + item.click(); + window.location = item.href; + } + } +} +function addLinks() { + res = [[], []]; + for (var l = 0; l < links.length; l++) { + var li = links[l]; + if (isVisible(li) && elementInViewport(li)) { + res[0].push(li); + res[1].push(li.innerText.toLowerCase()); + } + } + return res; +} +function addFormElems() { + res = [[], []]; + for (var f = 0; f < forms.length; f++) { + for (var e = 0; e < forms[f].elements.length; e++) { + var el = forms[f].elements[e]; + if (el && ['INPUT', 'TEXTAREA', 'SELECT'].indexOf(el.tagName) + 1 && isVisible(el) && elementInViewport(el)) { + res[0].push(el); + if (el.getAttribute('value')) { + res[1].push(el.getAttribute('value').toLowerCase()); + } else { + res[1].push(el.getAttribute('name').toLowerCase()); + } + } + } + } + return res; +} +function reDrawHints(elems, len) { + var hintdiv = doc.createElement('div'); + hintdiv.setAttribute('id', uzbldivid); + hintdiv.style.opacity = '0.0'; + for (var i = 0; i < elems[0].length; i++) { + var label = i + ''; + var n = label.length; + for (n; n < len; n++) { + label = '0' + label; + } + if (elems[0][i]) { + var h = generateHint(elems[0][i], label); + hintdiv.appendChild(h); + } + } + if (document.body) { + document.body.appendChild(hintdiv); + hintdiv.style.opacity = '0.7' + } +} +function followLinks(follow) { + var s = follow.split(''); + var linknr = parseInt(follow, 10); + if (document.body) document.body.setAttribute('onkeyup', 'keyPressHandler(event)'); + var linkelems = addLinks(); + var formelems = addFormElems(); + var elems = [linkelems[0].concat(formelems[0]), linkelems[1].concat(formelems[1])]; + var len = (elems[0].length + '').length; + var oldDiv = doc.getElementById(uzbldivid); + var leftover = [[], []]; + if (linknr + 1 && s.length == len && linknr < elems[0].length && linknr >= 0) { + clickElem(elems[0][linknr]); + } else { + for (var j = 0; j < elems[0].length; j++) { + var b = true; + for (var k = 0; k < s.length; k++) { + b = b && elems[1][j].charAt(k) == s[k]; + } + if (!b) { + elems[0][j] = null; + elems[1][j] = null; + } else { + leftover[0].push(elems[0][j]); + leftover[1].push(elems[1][j]); + } + } + if (leftover[0].length == 1) { + clickElem(leftover[0][0]); + } else if (!oldDiv) { + if (linknr + 1 || s.length == 0) { + reDrawHints(elems, len); + } else { + reDrawHints(leftover, len); + } + } + } +} +followLinks('%s'); diff --git a/.config/uzbl/scripts/formfiller.pl b/.config/uzbl/scripts/formfiller.pl new file mode 100755 index 0000000..23da347 --- /dev/null +++ b/.config/uzbl/scripts/formfiller.pl @@ -0,0 +1,99 @@ +#!/usr/bin/perl + +# a slightly more advanced form filler +# +# uses settings file like: $keydir/<domain> +#TODO: fallback to $HOME/.local/share +# user arg 1: +# edit: force editing of the file (fetches if file is missing) +# load: fill forms from file (fetches if file is missing) +# new: fetch new file + +# usage example: +# bind LL = spawn /usr/share/uzbl/examples/scripts/formfiller.pl load +# bind LN = spawn /usr/share/uzbl/examples/scripts/formfiller.pl new +# bind LE = spawn /usr/share/uzbl/examples/scripts/formfiller.pl edit + +use strict; +use warnings; + +my $keydir = $ENV{XDG_CONFIG_HOME} . "/uzbl/forms"; +my ($config,$pid,$xid,$fifoname,$socket,$url,$title,$cmd) = @ARGV; +if (!defined $fifoname || $fifoname eq "") { die "No fifo"; } + +sub domain { + my ($url) = @_; + $url =~ s#http(s)?://([A-Za-z0-9\.-]+)(/.*)?#$2#; + return $url; +}; + +my $editor = "xterm -e vim"; +#my $editor = "gvim"; + +# ideally, there would be some way to ask uzbl for the html content instead of having to redownload it with +# Also, you may need to fake the user-agent on some sites (like facebook) + my $downloader = "curl -A 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.10) Gecko/2009042810 GranParadiso/3.0.10' "; +#my $downloader = "curl -s"; + +my @fields = ("type","name","value"); + +my %command; + +$command{load} = sub { + my ($domain) = @_; + my $filename = "$keydir/$domain"; + if (-e $filename){ + open(my $file, $filename) or die "Failed to open $filename: $!"; + my (@lines) = <$file>; + close($file); + $|++; + open(my $fifo, ">>", $fifoname) or die "Failed to open $fifoname: $!"; + foreach my $line (@lines) { + next if ($line =~ m/^#/); + my ($type,$name,$value) = ($line =~ /^\s*(\w+)\s*\|\s*(.*?)\s*\|\s*(.*?)\s*$/); + if ($type eq "checkbox") + { + printf $fifo 'js document.getElementsByName("%s")[0].checked = %s;', $name, $value; + } elsif ($type eq "submit") + { + printf $fifo 'js function fs (n) {try{n.submit()} catch (e){fs(n.parentNode)}}; fs(document.getElementsByName("%s")[0]);', $name; + } elsif ($type ne "") + { + printf $fifo 'js document.getElementsByName("%s")[0].value = "%s";', $name, $value; + } + print $fifo "\n"; + } + $|--; + } else { + $command{new}->($domain); + $command{edit}->($domain); + } +}; +$command{edit} = sub { + my ($domain) = @_; + my $file = "$keydir/$domain"; + if(-e $file){ + system ($editor, $file); + } else { + $command{new}->($domain); + } +}; +$command{new} = sub { + my ($domain) = @_; + my $filename = "$keydir/$domain"; + open (my $file,">>", $filename) or die "Failed to open $filename: $!"; + $|++; + print $file "# Make sure that there are no extra submits, since it may trigger the wrong one.\n"; + printf $file "#%-10s | %-10s | %s\n", @fields; + print $file "#------------------------------\n"; + my @data = `$downloader $url`; + foreach my $line (@data){ + if($line =~ m/<input ([^>].*?)>/i){ + $line =~ s/.*(<input ([^>].*?)>).*/$1/; + printf $file " %-10s | %-10s | %s\n", map { my ($r) = $line =~ /.*$_=["'](.*?)["']/;$r } @fields; + }; + }; + $|--; +}; + +$command{$cmd}->(domain($url)); diff --git a/.config/uzbl/scripts/formfiller.sh b/.config/uzbl/scripts/formfiller.sh new file mode 100755 index 0000000..10afaba --- /dev/null +++ b/.config/uzbl/scripts/formfiller.sh @@ -0,0 +1,62 @@ +#!/bin/bash + +# simple html form (eg for logins) filler (and manager) for uzbl. +# uses settings files like: $keydir/<domain> +# files contain lines like: <fieldname>: <value> + + +# user arg 1: +# edit: force editing the file (falls back to new if not found) +# new: start with a new file. +# load: try to load from file into form + +# something else (or empty): if file not available: new, otherwise load. + +keydir=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/forms +[ -d "`dirname $keydir`" ] || exit 1 +[ -d "$keydir" ] || mkdir "$keydir" + +editor=${VISUAL} +if [[ -z ${editor} ]]; then + #editor='gvim' + editor='urxvt -e vim' +fi + +config=$1; shift +pid=$1; shift +xid=$1; shift +fifo=$1; shift +socket=$1; shift +url=$1; shift +title=$1; shift +action=$1 + +[ -d $keydir ] || mkdir $keydir || exit 1 + +if [ "$action" != 'edit' -a "$action" != 'new' -a "$action" != 'load' ] +then + action=new + [[ -e $keydir/$domain ]] && action=load +elif [ "$action" == 'edit' ] && [[ ! -e $keydir/$domain ]] +then + action=new +fi +domain=$(echo $url | sed -re 's|(http\|https)+://([A-Za-z0-9\.]+)/.*|\2|') + + +#regex='s|.*<input.*?name="([[:graph:]]+)".*?/>.*|\1: |p' # sscj's first version, does not work on http://wiki.archlinux.org/index.php?title=Special:UserLogin&returnto=Main_Page + regex='s|.*<input.*?name="([^"]*)".*|\1: |p' #works on arch wiki, but not on http://lists.uzbl.org/listinfo.cgi/uzbl-dev-uzbl.org TODO: improve + + +if [ "$action" = 'load' ] +then + [[ -e $keydir/$domain ]] || exit 2 + gawk -F': ' '{ print "js document.getElementsByName(\"" $1 "\")[0].value = \"" $2 "\";"}' $keydir/$domain >> $fifo +else + if [ "$action" == 'new' ] + then + curl "$url" | grep '<input' | sed -nre "$regex" > $keydir/$domain + fi + [[ -e $keydir/$domain ]] || exit 3 #this should never happen, but you never know. + $editor $keydir/$domain #TODO: if user aborts save in editor, the file is already overwritten +fi diff --git a/.config/uzbl/scripts/hint.js b/.config/uzbl/scripts/hint.js new file mode 100644 index 0000000..ec7f1e2 --- /dev/null +++ b/.config/uzbl/scripts/hint.js @@ -0,0 +1,26 @@ +for (var i=0; i < document.links.length; i++) { + var uzblid = 'uzbl_link_hint_'; + var li = document.links[i]; + var pre = document.getElementById(uzblid+i); + + if (pre) { + li.removeChild(pre); + } else { + var hint = document.createElement('div'); + hint.setAttribute('id',uzblid+i); + hint.innerHTML = i; + hint.style.display='inline'; + hint.style.lineHeight='90%'; + hint.style.backgroundColor='red'; + hint.style.color='white'; + hint.style.fontSize='small-xx'; + hint.style.fontWeight='light'; + hint.style.margin='0px'; + hint.style.padding='2px'; + hint.style.position='absolute'; + hint.style.textDecoration='none'; + hint.style.left=li.style.left; + hint.style.top=li.style.top; + li.insertAdjacentElement('afterBegin',hint); + } +} diff --git a/.config/uzbl/scripts/history.sh b/.config/uzbl/scripts/history.sh new file mode 100755 index 0000000..7c83aa6 --- /dev/null +++ b/.config/uzbl/scripts/history.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/history +[ -d `dirname $file` ] || exit 1 +echo `date +'%Y-%m-%d %H:%M:%S'`" $6 $7" >> $file diff --git a/.config/uzbl/scripts/insert_bookmark.sh b/.config/uzbl/scripts/insert_bookmark.sh new file mode 100755 index 0000000..e04e6d4 --- /dev/null +++ b/.config/uzbl/scripts/insert_bookmark.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +[ -d "${XDG_DATA_HOME:-$HOME/.local/share}/uzbl" ] || exit 1 +file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/bookmarks + +which zenity &>/dev/null || exit 2 + +entry=`zenity --entry --text="Add bookmark. add tags after the '\t', separated by spaces" --entry-text="$6 $7\t"` +url=`echo $entry | awk '{print $1}'` + +# TODO: check if already exists, if so, and tags are different: ask if you want to replace tags +echo "$entry" >/dev/null #for some reason we need this.. don't ask me why +echo -e "$entry" >> $file +true diff --git a/.config/uzbl/scripts/linkfollow.js b/.config/uzbl/scripts/linkfollow.js new file mode 100644 index 0000000..0eb629b --- /dev/null +++ b/.config/uzbl/scripts/linkfollow.js @@ -0,0 +1,269 @@ +// link follower for uzbl +// requires http://github.com/DuClare/uzbl/commit/6c11777067bdb8aac09bba78d54caea04f85e059 +// +// first, it needs to be loaded before every time it is used. +// One way would be to use the load_commit_handler: +// set load_commit_handler = sh 'echo "script /usr/share/uzbl/examples/scripts/linkfollow.js" > "$4"' +// +// when script is loaded, it can be invoked with +// bind f* = js hints.set("%s", hints.open) +// bind f_ = js hints.follow("%s",hints.open) +// +// At the moment, it may be useful to have way of forcing uzbl to load the script +// bind :lf = script /usr/share/uzbl/examples/scripts/linkfollow.js +// +// The default style for the hints are pretty ugly, so it is recommended to add the following +// to config file +// set stylesheet_uri = /usr/share/uzbl/examples/data/style.css +// +// based on follow_Numbers.js +// +// TODO: fix styling for the first element +// TODO: emulate mouseover events when visiting some elements +// TODO: rewrite the element->action handling + + +function Hints(){ + + // Settings + //////////////////////////////////////////////////////////////////////////// + + // if set to true, you must explicitly call hints.follow(), otherwise it will + // follow the link if there is only one matching result + var requireReturn = true; + + // Case sensitivity flag + var matchCase = "i"; + + // For case sensitive matching, uncomment: + // var matchCase = ""; + + + var uzblid = 'uzbl_hint'; + var uzblclass = 'uzbl_highlight'; + var uzblclassfirst = 'uzbl_h_first'; + var doc = document; + var visible = []; + var hintdiv; + + this.set = hint; + this.follow = follow; + this.keyPressHandler = keyPressHandler; + + function elementPosition(el) { + var up = el.offsetTop; + var left = el.offsetLeft; var width = el.offsetWidth; + var height = el.offsetHeight; + + while (el.offsetParent) { + el = el.offsetParent; + up += el.offsetTop; + left += el.offsetLeft; + } + return {up: up, left: left, width: width, height: height}; + } + + function elementInViewport(p) { + return (p.up < window.pageYOffset + window.innerHeight && + p.left < window.pageXOffset + window.innerWidth && + (p.up + p.height) > window.pageYOffset && + (p.left + p.width) > window.pageXOffset); + } + + function isVisible(el) { + if (el == doc) { return true; } + if (!el) { return false; } + if (!el.parentNode) { return false; } + if (el.style) { + if (el.style.display == 'none') { + return false; + } + if (el.style.visibility == 'hidden') { + return false; + } + } + return isVisible(el.parentNode); + } + + // the vimperator defaults minus the xhtml elements, since it gave DOM errors + var hintable = " //*[@onclick or @onmouseover or @onmousedown or @onmouseup or @oncommand or @class='lk' or @role='link' or @href] | //input[not(@type='hidden')] | //a | //area | //iframe | //textarea | //button | //select"; + + function Matcher(str){ + var numbers = str.replace(/[^\d]/g,""); + var words = str.replace(/\d/g,"").split(/\s+/).map(function (n) { return new RegExp(n,matchCase)}); + this.test = test; + this.toString = toString; + this.numbers = numbers; + function matchAgainst(element){ + if(element.node.nodeName == "INPUT"){ + return element.node.value; + } else { + return element.node.textContent; + } + } + function test(element) { + // test all the regexp + var item = matchAgainst(element); + return words.every(function (regex) { return item.match(regex)}); + } + } + + function HintElement(node,pos){ + + this.node = node; + this.isHinted = false; + this.position = pos; + this.num = 0; + + this.addHint = function (labelNum) { + // TODO: fix uzblclassfirst + if(!this.isHinted){ + this.node.className += " " + uzblclass; + } + this.isHinted = true; + + // create hint + var hintNode = doc.createElement('div'); + hintNode.name = uzblid; + hintNode.innerText = labelNum; + hintNode.style.left = this.position.left + 'px'; + hintNode.style.top = this.position.up + 'px'; + hintNode.style.position = "absolute"; + doc.body.firstChild.appendChild(hintNode); + + } + this.removeHint = function(){ + if(this.isHinted){ + var s = (this.num)?uzblclassfirst:uzblclass; + this.node.className = this.node.className.replace(new RegExp(" "+s,"g"),""); + this.isHinted = false; + } + } + } + + function createHintDiv(){ + var hintdiv = doc.getElementById(uzblid); + if(hintdiv){ + hintdiv.parentNode.removeChild(hintdiv); + } + hintdiv = doc.createElement("div"); + hintdiv.setAttribute('id',uzblid); + doc.body.insertBefore(hintdiv,doc.body.firstChild); + return hintdiv; + } + + function init(){ + // WHAT? + doc.body.setAttribute("onkeyup","hints.keyPressHandler(event)"); + hintdiv = createHintDiv(); + visible = []; + + var items = doc.evaluate(hintable,doc,null,XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,null); + for (var i = 0;i<items.snapshotLength;i++){ + var item = items.snapshotItem(i); + var pos = elementPosition(item); + if(isVisible && elementInViewport(elementPosition(item))){ + visible.push(new HintElement(item,pos)); + } + } + } + + function clear(){ + + visible.forEach(function (n) { n.removeHint(); } ); + hintdiv = doc.getElementById(uzblid); + while(hintdiv){ + hintdiv.parentNode.removeChild(hintdiv); + hintdiv = doc.getElementById(uzblid); + } + } + + function update(str,openFun) { + var match = new Matcher(str); + hintdiv = createHintDiv(); + var i = 1; + visible.forEach(function (n) { + if(match.test(n)) { + n.addHint(i); + i++; + } else { + n.removeHint(); + }}); + if(!requireReturn){ + if(i==2){ //only been incremented once + follow(str,openFun); + } + } + } + + function hint(str,openFun){ + if(str.length == 0) init(); + update(str,openFun); + } + + function keyPressHandler(e) { + var kC = window.event ? event.keyCode: e.keyCode; + var Esc = window.event ? 27 : e.DOM_VK_ESCAPE; + if (kC == Esc) { + clear(); + doc.body.removeAttribute("onkeyup"); + } + } + + this.openNewWindow = function(item){ + // TODO: this doesn't work yet + item.className += " uzbl_follow"; + window.open(item.href,"uzblnew",""); + } + this.open = function(item){ + simulateMouseOver(item); + item.className += " uzbl_follow"; + window.location = item.href; + } + + function simulateMouseOver(item){ + var evt = doc.createEvent("MouseEvents"); + evt.initMouseEvent("MouseOver",true,true, + doc.defaultView,1,0,0,0,0, + false,false,false,false,0,null); + return item.dispatchEvent(evt); + } + + + function follow(str,openFunction){ + var m = new Matcher(str); + var items = visible.filter(function (n) { return n.isHinted }); + clear(); + var num = parseInt(m.numbers,10); + if(num){ + var item = items[num-1].node; + } else { + var item = items[0].node; + } + if (item) { + var name = item.tagName; + if (name == 'A') { + if(item.click) {item.click()}; + openFunction(item); + } else if (name == 'INPUT') { + var type = item.getAttribute('type').toUpperCase(); + if (type == 'TEXT' || type == 'FILE' || type == 'PASSWORD') { + item.focus(); + item.select(); + } else { + item.click(); + } + } else if (name == 'TEXTAREA' || name == 'SELECT') { + item.focus(); + item.select(); + } else { + item.click(); + openFunction(item); + } + } + } +} + +var hints = new Hints(); + +// vim:set et sw=2: diff --git a/.config/uzbl/scripts/load_url_from_bookmarks.sh b/.config/uzbl/scripts/load_url_from_bookmarks.sh new file mode 100755 index 0000000..1e9f9e7 --- /dev/null +++ b/.config/uzbl/scripts/load_url_from_bookmarks.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +#NOTE: it's the job of the script that inserts bookmarks to make sure there are no dupes. + +file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/bookmarks +[ -r "$file" ] || exit +COLORS=" -nb #303030 -nf khaki -sb #CCFFAA -sf #303030" +if dmenu --help 2>&1 | grep -q '\[-rs\] \[-ni\] \[-nl\] \[-xs\]' +then + DMENU="dmenu -i -xs -rs -l 10" # vertical patch + # show tags as well + goto=`$DMENU $COLORS < $file | awk '{print $1}'` +else + DMENU="dmenu -i" + # because they are all after each other, just show the url, not their tags. + goto=`awk '{print $1}' $file | $DMENU $COLORS` +fi + +#[ -n "$goto" ] && echo "uri $goto" > $4 +[ -n "$goto" ] && echo "uri $goto" | socat - unix-connect:$5 diff --git a/.config/uzbl/scripts/load_url_from_history.sh b/.config/uzbl/scripts/load_url_from_history.sh new file mode 100755 index 0000000..62e02ac --- /dev/null +++ b/.config/uzbl/scripts/load_url_from_history.sh @@ -0,0 +1,24 @@ +#!/bin/sh + +history_file=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/history +[ -r "$history_file" ] || exit 1 + +# choose from all entries, sorted and uniqued +# goto=`awk '{print $3}' $history_file | sort -u | dmenu -i` +COLORS=" -nb #303030 -nf khaki -sb #CCFFAA -sf #303030" +if dmenu --help 2>&1 | grep -q '\[-rs\] \[-ni\] \[-nl\] \[-xs\]'; +then + DMENU="dmenu -i -xs -rs -l 10" # vertical patch + # choose an item in reverse order, showing also the date and page titles + # pick the last field from the first 3 fields. this way you can pick a url (prefixed with date & time) or type just a new url. + goto=`tac $history_file | $DMENU $COLORS | cut -d ' ' -f -3 | awk '{print $NF}'` +else + DMENU="dmenu -i" + # choose from all entries (no date or title), the first one being current url, and after that all others, sorted and uniqued, in ascending order + current=`tail -n 1 $history_file | awk '{print $3}'`; + goto=`(echo $current; awk '{print $3}' $history_file | grep -v "^$current\$" \ + | sort -u) | $DMENU $COLORS` +fi + +[ -n "$goto" ] && echo "uri $goto" > $4 +#[ -n "$goto" ] && echo "uri $goto" | socat - unix-connect:$5 diff --git a/.config/uzbl/scripts/scheme.py b/.config/uzbl/scripts/scheme.py new file mode 100755 index 0000000..7286703 --- /dev/null +++ b/.config/uzbl/scripts/scheme.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python + +import os, subprocess, sys, urlparse + +def detach_open(cmd): + # Thanks to the vast knowledge of Laurence Withers (lwithers) and this message: + # http://mail.python.org/pipermail/python-list/2006-November/587523.html + if not os.fork(): + null = os.open(os.devnull,os.O_WRONLY) + for i in range(3): os.dup2(null,i) + os.close(null) + subprocess.Popen(cmd) + print 'USED' + +if __name__ == '__main__': + uri = sys.argv[8] + u = urlparse.urlparse(uri) + if u.scheme == 'mailto': + detach_open(['xterm', '-e', 'mail %s' % u.path]) + elif u.scheme == 'xmpp': + detach_open(['gajim-remote', 'open_chat', uri]) + elif u.scheme == 'git': + detach_open(['git', 'clone', uri], cwd=os.path.expanduser('~/src')) diff --git a/.config/uzbl/scripts/session.sh b/.config/uzbl/scripts/session.sh new file mode 100755 index 0000000..22d48a2 --- /dev/null +++ b/.config/uzbl/scripts/session.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +# Very simple session manager for uzbl. When called with "endsession" as the +# argument, it'll backup $sessionfile, look for fifos in $fifodir and +# instruct each of them to store their current url in $sessionfile and +# terminate themselves. Run with "launch" as the argument and an instance of +# uzbl will be launched for each stored url. "endinstance" is used internally +# and doesn't need to be called manually at any point. +# Add a line like 'bind quit = /path/to/session.sh endsession' to your config + +[ -d ${XDG_DATA_HOME:-$HOME/.local/share}/uzbl ] || exit 1 +scriptfile=$0 # this script +sessionfile=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/session # the file in which the "session" (i.e. urls) are stored +configfile=${XDG_DATA_HOME:-$HOME/.local/share}/uzbl/config # uzbl configuration file +UZBL="uzbl -c $configfile" # add custom flags and whatever here. + +fifodir=/tmp # remember to change this if you instructed uzbl to put its fifos elsewhere +thisfifo="$4" +act="$8" +url="$6" + +if [ "$act." = "." ]; then + act="$1" +fi + + +case $act in + "launch" ) + urls=`cat $sessionfile` + if [ "$urls." = "." ]; then + $UZBL + else + for url in $urls; do + $UZBL --uri "$url" & + done + fi + exit 0 + ;; + + "endinstance" ) + if [ "$url" != "(null)" ]; then + echo "$url" >> $sessionfile; + fi + echo "exit" > "$thisfifo" + ;; + + "endsession" ) + mv "$sessionfile" "$sessionfile~" + for fifo in $fifodir/uzbl_fifo_*; do + if [ "$fifo" != "$thisfifo" ]; then + echo "spawn $scriptfile endinstance" > "$fifo" + fi + done + echo "spawn $scriptfile endinstance" > "$thisfifo" + ;; + + * ) echo "session manager: bad action" + echo "Usage: $scriptfile [COMMAND] where commands are:" + echo " launch - Restore a saved session or start a new one" + echo " endsession - Quit the running session. Must be called from uzbl" + ;; +esac diff --git a/.config/uzbl/scripts/uzbl_tabbed.py b/.config/uzbl/scripts/uzbl_tabbed.py new file mode 100755 index 0000000..cd5ef4f --- /dev/null +++ b/.config/uzbl/scripts/uzbl_tabbed.py @@ -0,0 +1,1474 @@ +#!/usr/bin/env python + +# Uzbl tabbing wrapper using a fifo socket interface +# Copyright (c) 2009, Tom Adams <tom@holizz.com> +# Copyright (c) 2009, Chris van Dijk <cn.vandijk@hotmail.com> +# Copyright (c) 2009, Mason Larobina <mason.larobina@gmail.com> +# +# 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/>. + + +# Author(s): +# Tom Adams <tom@holizz.com> +# Wrote the original uzbl_tabbed.py as a proof of concept. +# +# Chris van Dijk (quigybo) <cn.vandijk@hotmail.com> +# Made signifigant headway on the old uzbl_tabbing.py script on the +# uzbl wiki <http://www.uzbl.org/wiki/uzbl_tabbed> +# +# Mason Larobina <mason.larobina@gmail.com> +# Rewrite of the uzbl_tabbing.py script to use a fifo socket interface +# and inherit configuration options from the user's uzbl config. +# +# Contributor(s): +# mxey <mxey@ghosthacking.net> +# uzbl_config path now honors XDG_CONFIG_HOME if it exists. +# +# Romain Bignon <romain@peerfuse.org> +# Fix for session restoration code. +# +# Jake Probst <jake.probst@gmail.com> +# Wrote a patch that overflows tabs in the tablist on to new lines when +# running of room. +# +# Devon Jones <devon.jones@gmail.com> +# Fifo command bring_to_front which brings the gtk window to focus. + + +# Dependencies: +# pygtk - python bindings for gtk. +# pango - python bindings needed for text rendering & layout in gtk widgets. +# pygobject - GLib's GObject bindings for python. +# +# Optional dependencies: +# simplejson - save uzbl_tabbed.py sessions & presets in json. +# +# Note: I haven't included version numbers with this dependency list because +# I've only ever tested uzbl_tabbed.py on the latest stable versions of these +# packages in Gentoo's portage. Package names may vary on different systems. + + +# Configuration: +# Because this version of uzbl_tabbed is able to inherit options from your main +# uzbl configuration file you may wish to configure uzbl tabbed from there. +# Here is a list of configuration options that can be customised and some +# example values for each: +# +# General tabbing options: +# show_tablist = 1 +# show_gtk_tabs = 0 +# tablist_top = 1 +# gtk_tab_pos = (top|left|bottom|right) +# gtk_refresh = 1000 +# switch_to_new_tabs = 1 +# capture_new_windows = 1 +# multiline_tabs = 1 +# +# Tab title options: +# tab_titles = 1 +# new_tab_title = Loading +# max_title_len = 50 +# show_ellipsis = 1 +# +# Session options: +# save_session = 1 +# json_session = 0 +# session_file = $HOME/.local/share/uzbl/session +# +# Inherited uzbl options: +# fifo_dir = /tmp +# socket_dir = /tmp +# icon_path = $HOME/.local/share/uzbl/uzbl.png +# status_background = #303030 +# +# Misc options: +# window_size = 800,800 +# verbose = 0 +# +# And the key bindings: +# bind_new_tab = gn +# bind_tab_from_clip = gY +# bind_tab_from_uri = go _ +# bind_close_tab = gC +# bind_next_tab = gt +# bind_prev_tab = gT +# bind_goto_tab = gi_ +# bind_goto_first = g< +# bind_goto_last = g> +# bind_clean_slate = gQ +# bind_exit = gZ +# +# Session preset key bindings: +# bind_save_preset = gsave _ +# bind_load_preset = gload _ +# bind_del_preset = gdel _ +# bind_list_presets = glist +# +# And uzbl_tabbed.py takes care of the actual binding of the commands via each +# instances fifo socket. +# +# Custom tab styling: +# tab_colours = foreground = "#888" background = "#303030" +# tab_text_colours = foreground = "#bbb" +# selected_tab = foreground = "#fff" +# selected_tab_text = foreground = "green" +# tab_indicate_https = 1 +# https_colours = foreground = "#888" +# https_text_colours = foreground = "#9c8e2d" +# selected_https = foreground = "#fff" +# selected_https_text = foreground = "gold" +# +# How these styling values are used are soley defined by the syling policy +# handler below (the function in the config section). So you can for example +# turn the tab text colour Firetruck-Red in the event "error" appears in the +# tab title or some other arbitrary event. You may wish to make a trusted +# hosts file and turn tab titles of tabs visiting trusted hosts purple. + + +# Issues: +# - new windows are not caught and opened in a new tab. +# - when uzbl_tabbed.py crashes it takes all the children with it. +# - when a new tab is opened when using gtk tabs the tab button itself +# grabs focus from its child for a few seconds. +# - when switch_to_new_tabs is not selected the notebook page is +# maintained but the new window grabs focus (try as I might to stop it). + + +# Todo: +# - add command line options to use a different session file, not use a +# session file and or open a uri on starup. +# - ellipsize individual tab titles when the tab-list becomes over-crowded +# - add "<" & ">" arrows to tablist to indicate that only a subset of the +# currently open tabs are being displayed on the tablist. +# - add the small tab-list display when both gtk tabs and text vim-like +# tablist are hidden (I.e. [ 1 2 3 4 5 ]) +# - check spelling. +# - pass a uzbl socketid to uzbl_tabbed.py and have it assimilated into +# the collective. Resistance is futile! + + +import pygtk +import gtk +import subprocess +import os +import re +import time +import getopt +import pango +import select +import sys +import gobject +import socket +import random +import hashlib +import atexit +import types + +from gobject import io_add_watch, source_remove, timeout_add, IO_IN, IO_HUP +from signal import signal, SIGTERM, SIGINT +from optparse import OptionParser, OptionGroup + + +pygtk.require('2.0') + +_SCRIPTNAME = os.path.basename(sys.argv[0]) +def error(msg): + sys.stderr.write("%s: error: %s\n" % (_SCRIPTNAME, msg)) + +# ============================================================================ +# ::: Default configuration section :::::::::::::::::::::::::::::::::::::::::: +# ============================================================================ + +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) + +# Setup xdg paths. +DATA_DIR = os.path.join(xdghome('DATA', '.local/share/'), 'uzbl/') +CONFIG_DIR = os.path.join(xdghome('CONFIG', '.config/'), 'uzbl/') + +# Ensure uzbl xdg paths exist +for path in [DATA_DIR, CONFIG_DIR]: + if not os.path.exists(path): + os.makedirs(path) + +# Path to uzbl config +UZBL_CONFIG = os.path.join(CONFIG_DIR, 'config') +if not os.path.exists(UZBL_CONFIG): + error("cannot find uzbl config file at %r" % UZBL_CONFIG) + sys.exit(1) + +# All of these settings can be inherited from your uzbl config file. +config = { + # Tab options + 'show_tablist': True, # Show text uzbl like statusbar tab-list + 'show_gtk_tabs': False, # Show gtk notebook tabs + 'tablist_top': True, # Display tab-list at top of window + 'gtk_tab_pos': 'top', # Gtk tab position (top|left|bottom|right) + 'gtk_refresh': 1000, # Tablist refresh millisecond interval + 'switch_to_new_tabs': True, # Upon opening a new tab switch to it + 'capture_new_windows': True, # Use uzbl_tabbed to catch new windows + 'multiline_tabs': True, # Tabs overflow onto new tablist lines. + + # Tab title options + 'tab_titles': True, # Display tab titles (else only tab-nums) + 'new_tab_title': 'Loading', # New tab title + 'max_title_len': 50, # Truncate title at n characters + 'show_ellipsis': True, # Show ellipsis when truncating titles + + # Session options + 'save_session': True, # Save session in file when quit + 'json_session': False, # Use json to save session. + 'saved_sessions_dir': os.path.join(DATA_DIR, 'sessions/'), + 'session_file': os.path.join(DATA_DIR, 'session'), + + # Inherited uzbl options + 'fifo_dir': '/tmp', # Path to look for uzbl fifo. + 'socket_dir': '/tmp', # Path to look for uzbl socket. + 'icon_path': os.path.join(DATA_DIR, 'uzbl.png'), + 'status_background': "#303030", # Default background for all panels. + + # Misc options + 'window_size': "800,800", # width,height in pixels. + 'verbose': False, # Print verbose output. + + # Key bindings + 'bind_new_tab': 'gn', # Open new tab. + 'bind_tab_from_clip': 'gY', # Open tab from clipboard. + 'bind_tab_from_uri': 'go _', # Open new tab and goto entered uri. + 'bind_close_tab': 'gC', # Close tab. + 'bind_next_tab': 'gt', # Next tab. + 'bind_prev_tab': 'gT', # Prev tab. + 'bind_goto_tab': 'gi_', # Goto tab by tab-number (in title). + 'bind_goto_first': 'g<', # Goto first tab. + 'bind_goto_last': 'g>', # Goto last tab. + 'bind_clean_slate': 'gQ', # Close all tabs and open new tab. + 'bind_exit': 'gZ', # Exit nicely. + + # Session preset key bindings + 'bind_save_preset': 'gsave _', # Save session to file %s. + 'bind_load_preset': 'gload _', # Load preset session from file %s. + 'bind_del_preset': 'gdel _', # Delete preset session %s. + 'bind_list_presets': 'glist', # List all session presets. + + # Add custom tab style definitions to be used by the tab colour policy + # handler here. Because these are added to the config dictionary like + # any other uzbl_tabbed configuration option remember that they can + # be superseeded from your main uzbl config file. + 'tab_colours': 'foreground = "#888" background = "#303030"', + 'tab_text_colours': 'foreground = "#bbb"', + 'selected_tab': 'foreground = "#fff"', + 'selected_tab_text': 'foreground = "green"', + 'tab_indicate_https': True, + 'https_colours': 'foreground = "#888"', + 'https_text_colours': 'foreground = "#9c8e2d"', + 'selected_https': 'foreground = "#fff"', + 'selected_https_text': 'foreground = "gold"', + +} # End of config dict. + +# This is the tab style policy handler. Every time the tablist is updated +# this function is called to determine how to colourise that specific tab +# according the simple/complex rules as defined here. You may even wish to +# move this function into another python script and import it using: +# from mycustomtabbingconfig import colour_selector +# Remember to rename, delete or comment out this function if you do that. + +def colour_selector(tabindex, currentpage, uzbl): + '''Tablist styling policy handler. This function must return a tuple of + the form (tab style, text style).''' + + # Just as an example: + # if 'error' in uzbl.title: + # if tabindex == currentpage: + # return ('foreground="#fff"', 'foreground="red"') + # return ('foreground="#888"', 'foreground="red"') + + # Style tabs to indicate connected via https. + if config['tab_indicate_https'] and uzbl.uri.startswith("https://"): + if tabindex == currentpage: + return (config['selected_https'], config['selected_https_text']) + return (config['https_colours'], config['https_text_colours']) + + # Style to indicate selected. + if tabindex == currentpage: + return (config['selected_tab'], config['selected_tab_text']) + + # Default tab style. + return (config['tab_colours'], config['tab_text_colours']) + +# ============================================================================ +# ::: End of configuration section ::::::::::::::::::::::::::::::::::::::::::: +# ============================================================================ + +def echo(msg): + if config['verbose']: + sys.stderr.write("%s: %s\n" % (_SCRIPTNAME, msg)) + + +def readconfig(uzbl_config, config): + '''Loads relevant config from the users uzbl config file into the global + config dictionary.''' + + if not os.path.exists(uzbl_config): + error("Unable to load config %r" % uzbl_config) + return None + + # Define parsing regular expressions + isint = re.compile("^(\-|)[0-9]+$").match + findsets = re.compile("^set\s+([^\=]+)\s*\=\s*(.+)$",\ + re.MULTILINE).findall + + h = open(os.path.expandvars(uzbl_config), 'r') + rawconfig = h.read() + h.close() + + configkeys, strip = config.keys(), str.strip + for (key, value) in findsets(rawconfig): + key, value = strip(key), strip(value) + if key not in configkeys: continue + if isint(value): value = int(value) + config[key] = value + + # Ensure that config keys that relate to paths are expanded. + pathkeys = ['fifo_dir', 'socket_dir', 'session_file', 'icon_path', + 'saved_sessions_dir'] + for key in pathkeys: + config[key] = os.path.expandvars(config[key]) + + +def counter(): + '''To infinity and beyond!''' + + i = 0 + while True: + i += 1 + yield i + + +def escape(s): + '''Replaces html markup in tab titles that screw around with pango.''' + + for (split, glue) in [('&','&'), ('<', '<'), ('>', '>')]: + s = s.replace(split, glue) + return s + + +def gen_endmarker(): + '''Generates a random md5 for socket message-termination endmarkers.''' + + return hashlib.md5(str(random.random()*time.time())).hexdigest() + + +class UzblTabbed: + '''A tabbed version of uzbl using gtk.Notebook''' + + class UzblInstance: + '''Uzbl instance meta-data/meta-action object.''' + + def __init__(self, parent, tab, fifo_socket, socket_file, pid,\ + uri, title, switch): + + self.parent = parent + self.tab = tab + self.fifo_socket = fifo_socket + self.socket_file = socket_file + self.pid = pid + self.title = title + self.uri = uri + self.timers = {} + self._lastprobe = 0 + self._fifoout = [] + self._socketout = [] + self._socket = None + self._buffer = "" + # Switch to tab after loading + self._switch = switch + # fifo/socket files exists and socket connected. + self._connected = False + # The kill switch + self._kill = False + + # Message termination endmarker. + self._marker = gen_endmarker() + + # Gen probe commands string + probes = [] + probe = probes.append + probe('print uri %d @uri %s' % (self.pid, self._marker)) + probe('print title %d @<document.title>@ %s' % (self.pid,\ + self._marker)) + self._probecmds = '\n'.join(probes) + + # Enqueue keybinding config for child uzbl instance + self.parent.config_uzbl(self) + + + def flush(self, timer_call=False): + '''Flush messages from the socket-out and fifo-out queues.''' + + if self._kill: + if self._socket: + self._socket.close() + self._socket = None + + error("Flush called on dead tab.") + return False + + if len(self._fifoout): + if os.path.exists(self.fifo_socket): + h = open(self.fifo_socket, 'w') + while len(self._fifoout): + msg = self._fifoout.pop(0) + h.write("%s\n"%msg) + h.close() + + if len(self._socketout): + if not self._socket and os.path.exists(self.socket_file): + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + sock.connect(self.socket_file) + self._socket = sock + + if self._socket: + while len(self._socketout): + msg = self._socketout.pop(0) + self._socket.send("%s\n"%msg) + + if not self._connected and timer_call: + if not len(self._fifoout + self._socketout): + self._connected = True + + if timer_call in self.timers.keys(): + source_remove(self.timers[timer_call]) + del self.timers[timer_call] + + if self._switch: + self.grabfocus() + + return len(self._fifoout + self._socketout) + + + def grabfocus(self): + '''Steal parent focus and switch the notebook to my own tab.''' + + tabs = list(self.parent.notebook) + tabid = tabs.index(self.tab) + self.parent.goto_tab(tabid) + + + def probe(self): + '''Probes the client for information about its self.''' + + if self._connected: + self.send(self._probecmds) + self._lastprobe = time.time() + + + def write(self, msg): + '''Child fifo write function.''' + + self._fifoout.append(msg) + # Flush messages from the queue if able. + return self.flush() + + + def send(self, msg): + '''Child socket send function.''' + + self._socketout.append(msg) + # Flush messages from queue if able. + return self.flush() + + + def __init__(self): + '''Create tablist, window and notebook.''' + + # Store information about the applications fifo_socket. + self._fifo = None + + self._timers = {} + self._buffer = "" + self._killed = False + + # A list of the recently closed tabs + self._closed = [] + + # Holds metadata on the uzbl childen open. + self.tabs = {} + + # Generates a unique id for uzbl socket filenames. + self.next_pid = counter().next + + # Create main window + self.window = gtk.Window() + try: + window_size = map(int, config['window_size'].split(',')) + self.window.set_default_size(*window_size) + + except: + error("Invalid value for default_size in config file.") + + self.window.set_title("Uzbl Browser") + self.window.set_border_width(0) + + # Set main window icon + icon_path = config['icon_path'] + if os.path.exists(icon_path): + self.window.set_icon(gtk.gdk.pixbuf_new_from_file(icon_path)) + + else: + icon_path = '/usr/share/uzbl/examples/data/uzbl/uzbl.png' + if os.path.exists(icon_path): + self.window.set_icon(gtk.gdk.pixbuf_new_from_file(icon_path)) + + # Attach main window event handlers + self.window.connect("delete-event", self.quitrequest) + + # Create tab list + if config['show_tablist']: + vbox = gtk.VBox() + self.window.add(vbox) + ebox = gtk.EventBox() + self.tablist = gtk.Label() + + self.tablist.set_use_markup(True) + self.tablist.set_justify(gtk.JUSTIFY_LEFT) + self.tablist.set_line_wrap(False) + self.tablist.set_selectable(False) + self.tablist.set_padding(2,2) + self.tablist.set_alignment(0,0) + self.tablist.set_ellipsize(pango.ELLIPSIZE_END) + self.tablist.set_text(" ") + self.tablist.show() + ebox.add(self.tablist) + ebox.show() + bgcolor = gtk.gdk.color_parse(config['status_background']) + ebox.modify_bg(gtk.STATE_NORMAL, bgcolor) + + # Create notebook + self.notebook = gtk.Notebook() + self.notebook.set_show_tabs(config['show_gtk_tabs']) + + # Set tab position + allposes = {'left': gtk.POS_LEFT, 'right':gtk.POS_RIGHT, + 'top':gtk.POS_TOP, 'bottom':gtk.POS_BOTTOM} + if config['gtk_tab_pos'] in allposes.keys(): + self.notebook.set_tab_pos(allposes[config['gtk_tab_pos']]) + + self.notebook.set_show_border(False) + self.notebook.set_scrollable(True) + self.notebook.set_border_width(0) + + self.notebook.connect("page-removed", self.tab_closed) + self.notebook.connect("switch-page", self.tab_changed) + self.notebook.connect("page-added", self.tab_opened) + + self.notebook.show() + if config['show_tablist']: + if config['tablist_top']: + vbox.pack_start(ebox, False, False, 0) + vbox.pack_end(self.notebook, True, True, 0) + + else: + vbox.pack_start(self.notebook, True, True, 0) + vbox.pack_end(ebox, False, False, 0) + + vbox.show() + + else: + self.window.add(self.notebook) + + self.window.show() + self.wid = self.notebook.window.xid + # Generate the fifo socket filename. + fifo_filename = 'uzbltabbed_%d' % os.getpid() + self.fifo_socket = os.path.join(config['fifo_dir'], fifo_filename) + + # Now initialise the fifo socket at self.fifo_socket + self.init_fifo_socket() + + # If we are using sessions then load the last one if it exists. + if config['save_session']: + self.load_session() + + + def run(self): + '''UzblTabbed main function that calls the gtk loop.''' + + if not len(self.tabs): + self.new_tab() + + gtk_refresh = int(config['gtk_refresh']) + if gtk_refresh < 100: + gtk_refresh = 100 + + # Update tablist timer + timerid = timeout_add(gtk_refresh, self.update_tablist) + self._timers["update-tablist"] = timerid + + # Probe clients every second for window titles and location + timerid = timeout_add(gtk_refresh, self.probe_clients) + self._timers["probe-clients"] = timerid + + # Make SIGTERM act orderly. + signal(SIGTERM, lambda signum, stack_frame: self.terminate(SIGTERM)) + + # Catch keyboard interrupts + signal(SIGINT, lambda signum, stack_frame: self.terminate(SIGINT)) + + try: + gtk.main() + + except: + error("encounted error %r" % sys.exc_info()[1]) + + # Unlink fifo socket + self.unlink_fifo_socket() + + # Attempt to close all uzbl instances nicely. + self.quitrequest() + + # Allow time for all the uzbl instances to quit. + time.sleep(1) + + raise + + + def terminate(self, termsig=None): + '''Handle termination signals and exit safely and cleanly.''' + + # Not required but at least it lets the user know what killed his + # browsing session. + if termsig == SIGTERM: + error("caught SIGTERM signal") + + elif termsig == SIGINT: + error("caught keyboard interrupt") + + else: + error("caught unknown signal") + + error("commencing infanticide!") + + # Sends the exit signal to all uzbl instances. + self.quitrequest() + + + def init_fifo_socket(self): + '''Create interprocess communication fifo socket.''' + + if os.path.exists(self.fifo_socket): + if not os.access(self.fifo_socket, os.F_OK | os.R_OK | os.W_OK): + os.mkfifo(self.fifo_socket) + + else: + basedir = os.path.dirname(self.fifo_socket) + if not os.path.exists(basedir): + os.makedirs(basedir) + + os.mkfifo(self.fifo_socket) + + # Add event handlers for IO_IN & IO_HUP events. + self.setup_fifo_watchers() + + echo("listening at %r" % self.fifo_socket) + + # Add atexit register to destroy the socket on program termination. + atexit.register(self.unlink_fifo_socket) + + + def unlink_fifo_socket(self): + '''Unlink the fifo socket. Note: This function is called automatically + on exit by an atexit register.''' + + # Make sure the fifo_socket fd is closed. + self.close_fifo() + + # And unlink if the real fifo_socket exists. + if os.path.exists(self.fifo_socket): + os.unlink(self.fifo_socket) + echo("unlinked %r" % self.fifo_socket) + + + def close_fifo(self): + '''Remove all event handlers watching the fifo and close the fd.''' + + # Already closed + if self._fifo is None: return + + (fd, watchers) = self._fifo + os.close(fd) + + # Stop all gobject io watchers watching the fifo. + for gid in watchers: + source_remove(gid) + + self._fifo = None + + + def setup_fifo_watchers(self): + '''Open fifo socket fd and setup gobject IO_IN & IO_HUP event + handlers.''' + + # Close currently open fifo_socket fd and kill all watchers + self.close_fifo() + + fd = os.open(self.fifo_socket, os.O_RDONLY | os.O_NONBLOCK) + + # Add gobject io event handlers to the fifo socket. + watchers = [io_add_watch(fd, IO_IN, self.main_fifo_read),\ + io_add_watch(fd, IO_HUP, self.main_fifo_hangup)] + + self._fifo = (fd, watchers) + + + def main_fifo_hangup(self, fd, cb_condition): + '''Handle main fifo socket hangups.''' + + # Close old fd, open new fifo socket and add io event handlers. + self.setup_fifo_watchers() + + # Kill the gobject event handler calling this handler function. + return False + + + def main_fifo_read(self, fd, cb_condition): + '''Read from main fifo socket.''' + + self._buffer = os.read(fd, 1024) + temp = self._buffer.split("\n") + self._buffer = temp.pop() + cmds = [s.strip().split() for s in temp if len(s.strip())] + + for cmd in cmds: + try: + #print cmd + self.parse_command(cmd) + + except: + error("parse_command: invalid command %s" % ' '.join(cmd)) + raise + + return True + + + def probe_clients(self): + '''Probe all uzbl clients for up-to-date window titles and uri's.''' + + save_session = config['save_session'] + + sockd = {} + tabskeys = self.tabs.keys() + notebooklist = list(self.notebook) + + for tab in notebooklist: + if tab not in tabskeys: continue + uzbl = self.tabs[tab] + uzbl.probe() + if uzbl._socket: + sockd[uzbl._socket] = uzbl + + sockets = sockd.keys() + (reading, _, errors) = select.select(sockets, [], sockets, 0) + + for sock in reading: + uzbl = sockd[sock] + uzbl._buffer = sock.recv(1024).replace('\n',' ') + temp = uzbl._buffer.split(uzbl._marker) + self._buffer = temp.pop() + cmds = [s.strip().split() for s in temp if len(s.strip())] + for cmd in cmds: + try: + #print cmd + self.parse_command(cmd) + + except: + error("parse_command: invalid command %s" % ' '.join(cmd)) + raise + + return True + + + def parse_command(self, cmd): + '''Parse instructions from uzbl child processes.''' + + # Commands ( [] = optional, {} = required ) + # new [uri] + # open new tab and head to optional uri. + # close [tab-num] + # close current tab or close via tab id. + # next [n-tabs] + # open next tab or n tabs down. Supports negative indexing. + # prev [n-tabs] + # open prev tab or n tabs down. Supports negative indexing. + # goto {tab-n} + # goto tab n. + # first + # goto first tab. + # last + # goto last tab. + # title {pid} {document-title} + # updates tablist title. + # uri {pid} {document-location} + # updates tablist uri + # bring_to_front + # brings the gtk window to focus. + # exit + # exits uzbl_tabbed.py + + if cmd[0] == "new": + if len(cmd) == 2: + self.new_tab(cmd[1]) + + else: + self.new_tab() + + elif cmd[0] == "newfromclip": + uri = subprocess.Popen(['xclip','-selection','clipboard','-o'],\ + stdout=subprocess.PIPE).communicate()[0] + if uri: + self.new_tab(uri) + + elif cmd[0] == "close": + if len(cmd) == 2: + self.close_tab(int(cmd[1])) + + else: + self.close_tab() + + elif cmd[0] == "next": + if len(cmd) == 2: + self.next_tab(int(cmd[1])) + + else: + self.next_tab() + + elif cmd[0] == "prev": + if len(cmd) == 2: + self.prev_tab(int(cmd[1])) + + else: + self.prev_tab() + + elif cmd[0] == "goto": + self.goto_tab(int(cmd[1])) + + elif cmd[0] == "first": + self.goto_tab(0) + + elif cmd[0] == "last": + self.goto_tab(-1) + + elif cmd[0] in ["title", "uri"]: + if len(cmd) > 2: + uzbl = self.get_tab_by_pid(int(cmd[1])) + if uzbl: + old = getattr(uzbl, cmd[0]) + new = ' '.join(cmd[2:]) + setattr(uzbl, cmd[0], new) + if old != new: + self.update_tablist() + + else: + error("parse_command: no uzbl with pid %r" % int(cmd[1])) + + elif cmd[0] == "preset": + if len(cmd) < 3: + error("parse_command: invalid preset command") + + elif cmd[1] == "save": + path = os.path.join(config['saved_sessions_dir'], cmd[2]) + self.save_session(path) + + elif cmd[1] == "load": + path = os.path.join(config['saved_sessions_dir'], cmd[2]) + self.load_session(path) + + elif cmd[1] == "del": + path = os.path.join(config['saved_sessions_dir'], cmd[2]) + if os.path.isfile(path): + os.remove(path) + + else: + error("parse_command: preset %r does not exist." % path) + + elif cmd[1] == "list": + uzbl = self.get_tab_by_pid(int(cmd[2])) + if uzbl: + if not os.path.isdir(config['saved_sessions_dir']): + js = "js alert('No saved presets.');" + uzbl.send(js) + + else: + listdir = os.listdir(config['saved_sessions_dir']) + listdir = "\\n".join(listdir) + js = "js alert('Session presets:\\n\\n%s');" % listdir + uzbl.send(js) + + else: + error("parse_command: unknown tab pid.") + + else: + error("parse_command: unknown parse command %r"\ + % ' '.join(cmd)) + + elif cmd[0] == "bring_to_front": + self.window.present() + + elif cmd[0] == "clean": + self.clean_slate() + + elif cmd[0] == "exit": + self.quitrequest() + + else: + error("parse_command: unknown command %r" % ' '.join(cmd)) + + + def get_tab_by_pid(self, pid): + '''Return uzbl instance by pid.''' + + for (tab, uzbl) in self.tabs.items(): + if uzbl.pid == pid: + return uzbl + + return False + + + def new_tab(self, uri='', title='', switch=None): + '''Add a new tab to the notebook and start a new instance of uzbl. + Use the switch option to negate config['switch_to_new_tabs'] option + when you need to load multiple tabs at a time (I.e. like when + restoring a session from a file).''' + + pid = self.next_pid() + tab = gtk.Socket() + tab.show() + self.notebook.append_page(tab) + sid = tab.get_id() + uri = uri.strip() + + fifo_filename = 'uzbl_fifo_%s_%0.2d' % (self.wid, pid) + fifo_socket = os.path.join(config['fifo_dir'], fifo_filename) + socket_filename = 'uzbl_socket_%s_%0.2d' % (self.wid, pid) + socket_file = os.path.join(config['socket_dir'], socket_filename) + + if switch is None: + switch = config['switch_to_new_tabs'] + + if not title: + title = config['new_tab_title'] + + uzbl = self.UzblInstance(self, tab, fifo_socket, socket_file, pid,\ + uri, title, switch) + + if len(uri): + uri = "--uri %r" % uri + + self.tabs[tab] = uzbl + cmd = 'uzbl -s %s -n %s_%0.2d %s &' % (sid, self.wid, pid, uri) + subprocess.Popen([cmd], shell=True) # TODO: do i need close_fds=True ? + + # Add gobject timer to make sure the config is pushed when fifo socket + # has been created. + timerid = timeout_add(100, uzbl.flush, "flush-initial-config") + uzbl.timers['flush-initial-config'] = timerid + + self.update_tablist() + + + def clean_slate(self): + '''Close all open tabs and open a fresh brand new one.''' + + self.new_tab() + tabs = self.tabs.keys() + for tab in list(self.notebook)[:-1]: + if tab not in tabs: continue + uzbl = self.tabs[tab] + uzbl.send("exit") + + + def config_uzbl(self, uzbl): + '''Send bind commands for tab new/close/next/prev to a uzbl + instance.''' + + binds = [] + bind_format = r'bind %s = sh "echo \"%s\" > \"%s\""' + bind = lambda key, action: binds.append(bind_format % (key, action,\ + self.fifo_socket)) + + sets = [] + set_format = r'set %s = sh \"echo \\"%s\\" > \\"%s\\""' + set = lambda key, action: binds.append(set_format % (key, action,\ + self.fifo_socket)) + + # Bind definitions here + # bind(key, command back to fifo) + bind(config['bind_new_tab'], 'new') + bind(config['bind_tab_from_clip'], 'newfromclip') + bind(config['bind_tab_from_uri'], 'new %s') + bind(config['bind_close_tab'], 'close') + bind(config['bind_next_tab'], 'next') + bind(config['bind_prev_tab'], 'prev') + bind(config['bind_goto_tab'], 'goto %s') + bind(config['bind_goto_first'], 'goto 0') + bind(config['bind_goto_last'], 'goto -1') + bind(config['bind_clean_slate'], 'clean') + bind(config['bind_save_preset'], 'preset save %s') + bind(config['bind_load_preset'], 'preset load %s') + bind(config['bind_del_preset'], 'preset del %s') + bind(config['bind_list_presets'], 'preset list %d' % uzbl.pid) + bind(config['bind_exit'], 'exit') + + # Set definitions here + # set(key, command back to fifo) + if config['capture_new_windows']: + set("new_window", r'new $8') + + # Send config to uzbl instance via its socket file. + uzbl.send("\n".join(binds+sets)) + + + def goto_tab(self, index): + '''Goto tab n (supports negative indexing).''' + + tabs = list(self.notebook) + if 0 <= index < len(tabs): + self.notebook.set_current_page(index) + self.update_tablist() + return None + + try: + tab = tabs[index] + # Update index because index might have previously been a + # negative index. + index = tabs.index(tab) + self.notebook.set_current_page(index) + self.update_tablist() + + except IndexError: + pass + + + def next_tab(self, step=1): + '''Switch to next tab or n tabs right.''' + + if step < 1: + error("next_tab: invalid step %r" % step) + return None + + ntabs = self.notebook.get_n_pages() + tabn = (self.notebook.get_current_page() + step) % ntabs + self.notebook.set_current_page(tabn) + self.update_tablist() + + + def prev_tab(self, step=1): + '''Switch to prev tab or n tabs left.''' + + if step < 1: + error("prev_tab: invalid step %r" % step) + return None + + ntabs = self.notebook.get_n_pages() + tabn = self.notebook.get_current_page() - step + while tabn < 0: tabn += ntabs + self.notebook.set_current_page(tabn) + self.update_tablist() + + + def close_tab(self, tabn=None): + '''Closes current tab. Supports negative indexing.''' + + if tabn is None: + tabn = self.notebook.get_current_page() + + else: + try: + tab = list(self.notebook)[tabn] + + except IndexError: + error("close_tab: invalid index %r" % tabn) + return None + + self.notebook.remove_page(tabn) + + + def tab_opened(self, notebook, tab, index): + '''Called upon tab creation. Called by page-added signal.''' + + if config['switch_to_new_tabs']: + self.notebook.set_focus_child(tab) + + else: + oldindex = self.notebook.get_current_page() + oldtab = self.notebook.get_nth_page(oldindex) + self.notebook.set_focus_child(oldtab) + + + def tab_closed(self, notebook, tab, index): + '''Close the window if no tabs are left. Called by page-removed + signal.''' + + if tab in self.tabs.keys(): + uzbl = self.tabs[tab] + for (timer, gid) in uzbl.timers.items(): + error("tab_closed: removing timer %r" % timer) + source_remove(gid) + del uzbl.timers[timer] + + if uzbl._socket: + uzbl._socket.close() + uzbl._socket = None + + uzbl._fifoout = [] + uzbl._socketout = [] + uzbl._kill = True + self._closed.append((uzbl.uri, uzbl.title)) + self._closed = self._closed[-10:] + del self.tabs[tab] + + if self.notebook.get_n_pages() == 0: + if not self._killed and config['save_session']: + if os.path.exists(config['session_file']): + os.remove(config['session_file']) + + self.quit() + + self.update_tablist() + + return True + + + def tab_changed(self, notebook, page, index): + '''Refresh tab list. Called by switch-page signal.''' + + tab = self.notebook.get_nth_page(index) + self.notebook.set_focus_child(tab) + self.update_tablist(index) + return True + + + def update_tablist(self, curpage=None): + '''Upate tablist status bar.''' + + show_tablist = config['show_tablist'] + show_gtk_tabs = config['show_gtk_tabs'] + tab_titles = config['tab_titles'] + show_ellipsis = config['show_ellipsis'] + multiline_tabs = config['multiline_tabs'] + + if multiline_tabs: + multiline = [] + + if not show_tablist and not show_gtk_tabs: + return True + + tabs = self.tabs.keys() + if curpage is None: + curpage = self.notebook.get_current_page() + + title_format = "%s - Uzbl Browser" + max_title_len = config['max_title_len'] + + if show_tablist: + pango = "" + normal = (config['tab_colours'], config['tab_text_colours']) + selected = (config['selected_tab'], config['selected_tab_text']) + + if tab_titles: + tab_format = "<span %s> [ %d <span %s> %s</span> ] </span>" + + else: + tab_format = "<span %s> [ <span %s>%d</span> ] </span>" + + if show_gtk_tabs: + gtk_tab_format = "%d %s" + + for index, tab in enumerate(self.notebook): + if tab not in tabs: continue + uzbl = self.tabs[tab] + + if index == curpage: + self.window.set_title(title_format % uzbl.title) + + # Unicode heavy strings do not like being truncated/sliced so by + # re-encoding the string sliced of limbs are removed. + tabtitle = uzbl.title[:max_title_len + int(show_ellipsis)] + if type(tabtitle) != types.UnicodeType: + tabtitle = unicode(tabtitle, 'utf-8', 'ignore') + + tabtitle = tabtitle.encode('utf-8', 'ignore').strip() + + if show_ellipsis and len(tabtitle) != len(uzbl.title): + tabtitle += "\xe2\x80\xa6" + + if show_gtk_tabs: + if tab_titles: + self.notebook.set_tab_label_text(tab, + gtk_tab_format % (index, tabtitle)) + + else: + self.notebook.set_tab_label_text(tab, str(index)) + + if show_tablist: + style = colour_selector(index, curpage, uzbl) + (tabc, textc) = style + + if multiline_tabs: + opango = pango + + if tab_titles: + pango += tab_format % (tabc, index, textc, + escape(tabtitle)) + + else: + pango += tab_format % (tabc, textc, index) + + self.tablist.set_markup(pango) + listwidth = self.tablist.get_layout().get_pixel_size()[0] + winwidth = self.window.get_size()[0] + + if listwidth > (winwidth - 20): + multiline.append(opango) + pango = tab_format % (tabc, index, textc, + escape(tabtitle)) + + elif tab_titles: + pango += tab_format % (tabc, index, textc, + escape(tabtitle)) + + else: + pango += tab_format % (tabc, textc, index) + + if show_tablist: + if multiline_tabs: + multiline.append(pango) + self.tablist.set_markup(' '.join(multiline)) + + else: + self.tablist.set_markup(pango) + + return True + + + def save_session(self, session_file=None): + '''Save the current session to file for restoration on next load.''' + + strip = str.strip + + if session_file is None: + session_file = config['session_file'] + + tabs = self.tabs.keys() + state = [] + for tab in list(self.notebook): + if tab not in tabs: continue + uzbl = self.tabs[tab] + if not uzbl.uri: continue + state += [(uzbl.uri, uzbl.title),] + + session = {'curtab': self.notebook.get_current_page(), + 'tabs': state} + + if config['json_session']: + raw = json.dumps(session) + + else: + lines = ["curtab = %d" % session['curtab'],] + for (uri, title) in session['tabs']: + lines += ["%s\t%s" % (strip(uri), strip(title)),] + + raw = "\n".join(lines) + + if not os.path.isfile(session_file): + dirname = os.path.dirname(session_file) + if not os.path.isdir(dirname): + os.makedirs(dirname) + + h = open(session_file, 'w') + h.write(raw) + h.close() + + + def load_session(self, session_file=None): + '''Load a saved session from file.''' + + default_path = False + strip = str.strip + json_session = config['json_session'] + delete_loaded = False + + if session_file is None: + default_path = True + delete_loaded = True + session_file = config['session_file'] + + if not os.path.isfile(session_file): + return False + + h = open(session_file, 'r') + raw = h.read() + h.close() + if json_session: + if sum([1 for s in raw.split("\n") if strip(s)]) != 1: + error("Warning: The session file %r does not look json. "\ + "Trying to load it as a non-json session file."\ + % session_file) + json_session = False + + if json_session: + try: + session = json.loads(raw) + curtab, tabs = session['curtab'], session['tabs'] + + except: + error("Failed to load jsonifed session from %r"\ + % session_file) + return None + + else: + tabs = [] + strip = str.strip + curtab, tabs = 0, [] + lines = [s for s in raw.split("\n") if strip(s)] + if len(lines) < 2: + error("Warning: The non-json session file %r looks invalid."\ + % session_file) + return None + + try: + for line in lines: + if line.startswith("curtab"): + curtab = int(line.split()[-1]) + + else: + uri, title = line.split("\t",1) + tabs += [(strip(uri), strip(title)),] + + except: + error("Warning: failed to load session file %r" % session_file) + return None + + session = {'curtab': curtab, 'tabs': tabs} + + # Now populate notebook with the loaded session. + for (index, (uri, title)) in enumerate(tabs): + self.new_tab(uri=uri, title=title, switch=(curtab==index)) + + # A saved session has been loaded now delete it. + if delete_loaded and os.path.exists(session_file): + os.remove(session_file) + + # There may be other state information in the session dict of use to + # other functions. Of course however the non-json session object is + # just a dummy object of no use to no one. + return session + + + def quitrequest(self, *args): + '''Attempt to close all uzbl instances nicely and exit.''' + + self._killed = True + + if config['save_session']: + if len(list(self.notebook)) > 1: + self.save_session() + + else: + # Notebook has one page open so delete the session file. + if os.path.isfile(config['session_file']): + os.remove(config['session_file']) + + for (tab, uzbl) in self.tabs.items(): + uzbl.send("exit") + + # Add a gobject timer to make sure the application force-quits after a + # reasonable period. Calling quit when all the tabs haven't had time to + # close should be a last resort. + timer = "force-quit" + timerid = timeout_add(5000, self.quit, timer) + self._timers[timer] = timerid + + + def quit(self, *args): + '''Cleanup and quit. Called by delete-event signal.''' + + # Close the fifo socket, remove any gobject io event handlers and + # delete socket. + self.unlink_fifo_socket() + + # Remove all gobject timers that are still ticking. + for (timerid, gid) in self._timers.items(): + source_remove(gid) + del self._timers[timerid] + + try: + gtk.main_quit() + + except: + pass + + +if __name__ == "__main__": + + # Read from the uzbl config into the global config dictionary. + readconfig(UZBL_CONFIG, config) + + # Build command line parser + usage = "usage: %prog [OPTIONS] {URIS}..." + parser = OptionParser(usage=usage) + parser.add_option('-n', '--no-session', dest='nosession', + action='store_true', help="ignore session saving a loading.") + parser.add_option('-v', '--verbose', dest='verbose', + action='store_true', help='print verbose output.') + + # Parse command line options + (options, uris) = parser.parse_args() + + if options.nosession: + config['save_session'] = False + + if options.verbose: + config['verbose'] = True + + if config['json_session']: + try: + import simplejson as json + + except: + error("Warning: json_session set but cannot import the python "\ + "module simplejson. Fix: \"set json_session = 0\" or "\ + "install the simplejson python module to remove this warning.") + config['json_session'] = False + + if config['verbose']: + import pprint + sys.stderr.write("%s\n" % pprint.pformat(config)) + + uzbl = UzblTabbed() + + # All extra arguments given to uzbl_tabbed.py are interpreted as + # web-locations to opened in new tabs. + lasturi = len(uris)-1 + for (index,uri) in enumerate(uris): + uzbl.new_tab(uri, switch=(index==lasturi)) + + uzbl.run() diff --git a/.config/uzbl/scripts/uzblcat b/.config/uzbl/scripts/uzblcat new file mode 100755 index 0000000..e955608 --- /dev/null +++ b/.config/uzbl/scripts/uzblcat @@ -0,0 +1,12 @@ +#!/usr/bin/env python +# uzblcat - safely push html to uzbl +# See http://www.uzbl.org/wiki/html-mode + +from sys import stdin, stdout + +stdout.write("uri data:text/html,") +for line in stdin: + stdout.write(line[0:-1]) + +# vim: set noet ff=unix + diff --git a/.config/uzbl/scripts/yank.sh b/.config/uzbl/scripts/yank.sh new file mode 100755 index 0000000..376b7e2 --- /dev/null +++ b/.config/uzbl/scripts/yank.sh @@ -0,0 +1,17 @@ +#!/bin/sh +# use this script to pipe any variable to xclip, so you have it in your clipboard +# in your uzbl config, make the first argument the number of the (later) argument you want to use (see README for list of args) +# make the 2nd argument one of : primary, secondary, clipboard. +# examples: +# bind yurl = spawn ./examples/scripts/yank.sh 6 primary +# bind ytitle = spawn ./examples/scripts/yank.sh 7 clipboard + +clip=xclip + +which $clip &>/dev/null || exit 1 +[ "x$9" = xprimary -o "x$9" = xsecondary -o "x$9" = xclipboard ] || exit 2 + +value=`eval "echo -n \\${$8}"` # bash: value = ${!8} + +echo "echo -n '${value}' | $clip -selection $9" +echo -n "'${value}' | $clip -selection $9" diff --git a/.config/uzbl/style.css b/.config/uzbl/style.css new file mode 100644 index 0000000..f9b111e --- /dev/null +++ b/.config/uzbl/style.css @@ -0,0 +1,25 @@ +.uzbl_highlight { background-color: yellow;} +.uzbl_h_first { background-color: lightgreen;} + +.uzbl_follow { border-style: dotted; + border-width: thin; +} + +#uzbl_hint > div { + display: inline; + border: 2px solid #4a6600; + background-color: #b9ff00; + color: black; + font-size: 9px; + font-weight: bold; + line-height: 9px; + margin: 0px; + padding: 0px; + position: absolute; + z-index: 1000; + -webkit-border-radius: 6px; + text-decoration: none; + -wekit-transform: scale(1) rotate(0deg) translate(-6px,-5px); +} + +/* vim:set et ts=4: */ diff --git a/.config/uzbl/uzbl.png b/.config/uzbl/uzbl.png Binary files differnew file mode 100644 index 0000000..773ea84 --- /dev/null +++ b/.config/uzbl/uzbl.png |