Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'develop'

  • Loading branch information...
commit e982ac6d2b2e576c4bcfa586b9e5061a9fc8879f 2 parents 985bfd8 + 5004307
Mason Larobina mason-larobina authored
22 AUTHORS
... ... @@ -1,16 +1,24 @@
1   -Luakit project:
2   - Mason Larobina (mason-l) <mason.larobina<AT>gmail.com> 2010
  1 +Authors in the luakit project:
  2 + Clint Adams (Clint) <schizo<AT>debian.org> 2010
  3 + Gregor Uhlenheuer (kongo2002) <kongo2002<AT>googlemail.com> 2010
  4 + Mason Larobina (mason-l) <mason.larobina<AT>gmail.com> 2010
  5 + Pawel Zuzelski (pawelz) <pawelz<AT>pld-linux.org> 2010
  6 +
  7 +Author of `scripts/follow.js`:
  8 + Fabian Streitel <karottenreibe<AT>gmail.com> 2010
3 9
4 10 Inherited authors from the awesomewm project:
5   - Julien Danjou <julien<AT>danjou.info> 2007-2009
6   - Pierre Habouzit <madcoder<AT>debian.org> 2008
  11 + Julien Danjou <julien<AT>danjou.info> 2007-2009
  12 + Pierre Habouzit <madcoder<AT>debian.org> 2008
7 13
8 14 Inherited authors from the uzbl project:
9   - Robert Manea (robm) <rob.manea<AT>gmail.com> 2009-2010
10   - Dieter Plaetinck (Dieter@be) <dieter<AT>plaetinck.be> 2009-2010
  15 + Dequis <dx<AT>dxzone.com.ar> 2009
  16 + Dieter Plaetinck (Dieter@be) <dieter<AT>plaetinck.be> 2009-2010
  17 + Robert Manea (robm) <rob.manea<AT>gmail.com> 2009-2010
  18 + Simon Lipp (sloonz) <sloonz<AT>gmail.com> 2010
11 19
12 20 Inherited authors from the surf project:
13   - Enno Boland <g<AT>s01.de> 2009
  21 + Enno Boland (tox) <tox<AT>s01.de> 2009-2010
14 22
15 23 If you believe you should be in this file or notice that I have missed an
16 24 attribution to somebody else please contact me (Mason) or email me a patch
12 COPYING
... ... @@ -1,13 +1,13 @@
1 1 See AUTHORS file for list of copyright holders.
2 2
3   -Several source files are based on files in the surf micro-browser project and
4   -those sections are licensed under the MIT licence (see COPYING.MIT).
  3 +Several source files are based on files in the surf micro-browser project
  4 +and those sections have been re-licensed under the GPLv3 licence
  5 +(see COPYING.GPLv3).
5 6
6   -Several source files are based on files in the uzbl micro-browser project and
7   -those sections and are licensed under the GPLv3 license (see COPYING.GPLv3).
  7 +Several source files are based on files in the uzbl micro-browser project
  8 +and those sections are licensed under the GPLv3 license (see COPYING.GPLv3).
8 9
9 10 Several source files are based on files in the awesome window-manager project
10 11 and those sections are licensed under the GPLv3 licence (see COPYING.GPLv3).
11 12
12   -Everything else is GPLv3 (see COPYING.GPLv3) unless otherwise mentioned at
13   -the beginning of the file.
  13 +Everything else is GPLv3 (see COPYING.GPLv3).
21 COPYING.MIT
... ... @@ -1,21 +0,0 @@
1   -MIT/X Consortium License
2   -
3   -(C) 2009 Enno Boland <gottox@s01.de>
4   -
5   -Permission is hereby granted, free of charge, to any person obtaining a
6   -copy of this software and associated documentation files (the "Software"),
7   -to deal in the Software without restriction, including without limitation
8   -the rights to use, copy, modify, merge, publish, distribute, sublicense,
9   -and/or sell copies of the Software, and to permit persons to whom the
10   -Software is furnished to do so, subject to the following conditions:
11   -
12   -The above copyright notice and this permission notice shall be included in
13   -all copies or substantial portions of the Software.
14   -
15   -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16   -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17   -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18   -THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19   -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20   -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21   -DEALINGS IN THE SOFTWARE.
4 Makefile
@@ -35,7 +35,7 @@ ${GSRC} ${GHEAD}: ${GPERF}
35 35 @${CC} -c ${CFLAGS} ${CPPFLAGS} $< -o $@
36 36
37 37 globalconf.h: globalconf.h.in
38   - sed 's#LUAKIT_LUA_LIB_PATH .*#LUAKIT_LUA_LIB_PATH "$(PREFIX)/share/luakit/lib"#' globalconf.h.in > globalconf.h
  38 + sed 's#LUAKIT_INSTALL_PATH .*#LUAKIT_INSTALL_PATH "$(PREFIX)/share/luakit"#' globalconf.h.in > globalconf.h
39 39
40 40 ${OBJS}: ${HEADS} config.mk globalconf.h
41 41
@@ -52,6 +52,8 @@ install:
52 52 install -m644 README.md AUTHORS COPYING* $(DOCDIR)
53 53 cp -r lib/ $(INSTALLDIR)/share/luakit/
54 54 chmod -R 755 $(INSTALLDIR)/share/luakit/lib/
  55 + cp -r scripts/ $(INSTALLDIR)/share/luakit/
  56 + chmod -R 755 $(INSTALLDIR)/share/luakit/scripts/
55 57 install -D luakit $(INSTALLDIR)/bin/luakit
56 58 install -d $(DESTDIR)/etc/xdg/luakit/
57 59 install -D rc.lua $(DESTDIR)/etc/xdg/luakit/rc.lua
2  common/luaclass.c
@@ -314,4 +314,4 @@ luaH_class_new(lua_State *L, lua_class_t *lua_class) {
314 314 return 1;
315 315 }
316 316
317   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  317 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  common/luaclass.h
@@ -121,4 +121,4 @@ luaH_checkudataornil(lua_State *L, gint udx, lua_class_t *class) {
121 121
122 122 #endif
123 123
124   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  124 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  common/lualib.h
@@ -118,4 +118,4 @@ luaH_dofunction(lua_State *L, gint nargs, gint nret) {
118 118
119 119 #endif
120 120
121   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  121 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  common/luaobject.c
@@ -296,4 +296,4 @@ luaH_object_gc(lua_State *L) {
296 296 return 0;
297 297 }
298 298
299   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  299 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  common/luaobject.h
@@ -193,4 +193,4 @@ gint luaH_object_gc(lua_State *);
193 193
194 194 #endif
195 195
196   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  196 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  common/signal.h
@@ -114,4 +114,4 @@ signal_remove(signal_t *signals, const gchar *name, gpointer ref) {
114 114 }
115 115
116 116 #endif
117   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  117 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
3  common/tokenize.gperf
@@ -6,6 +6,7 @@ count
6 6 current
7 7 destroy
8 8 entry
  9 +eval_js
9 10 eventbox
10 11 fg
11 12 focus
@@ -27,6 +28,7 @@ hovered_uri
27 28 icon
28 29 indexof
29 30 insert
  31 +install_path
30 32 label
31 33 loading
32 34 notebook
@@ -35,6 +37,7 @@ pack_start
35 37 remove
36 38 search
37 39 selectable
  40 +selection
38 41 set_alignment
39 42 set_child
40 43 set_padding
15 common/util.c
@@ -24,6 +24,7 @@
24 24 #include <stdarg.h>
25 25 #include <stdlib.h>
26 26
  27 +#include "globalconf.h"
27 28 #include "common/util.h"
28 29
29 30 /* Print error and exit with EXIT_FAILURE code. */
@@ -52,12 +53,14 @@ _warn(int line, const char *fct, const char *fmt, ...) {
52 53 /* Print debug message on stderr. */
53 54 void
54 55 _debug(int line, const char *fct, const char *fmt, ...) {
55   - va_list ap;
56   - va_start(ap, fmt);
57   - g_fprintf(stderr, "D: luakit: %s:%d: ", fct, line);
58   - g_vfprintf(stderr, fmt, ap);
59   - va_end(ap);
60   - g_fprintf(stderr, "\n");
  56 + if (globalconf.verbose) {
  57 + va_list ap;
  58 + va_start(ap, fmt);
  59 + g_fprintf(stderr, "D: luakit: %s:%d: ", fct, line);
  60 + g_vfprintf(stderr, fmt, ap);
  61 + va_end(ap);
  62 + g_fprintf(stderr, "\n");
  63 + }
61 64 }
62 65
63 66 gboolean
10 common/util.h
@@ -37,17 +37,9 @@ void _fatal(int, const gchar *, const gchar *, ...);
37 37 #define warn(string, ...) _warn(__LINE__, __FUNCTION__, string, ##__VA_ARGS__)
38 38 void _warn(int, const gchar *, const gchar *, ...);
39 39
40   -#ifdef DEBUG_MESSAGES
41   -
42 40 #define debug(string, ...) _debug(__LINE__, __FUNCTION__, string, ##__VA_ARGS__)
43 41 void _debug(int, const gchar *, const gchar *, ...);
44 42
45   -#else
46   -
47   -#define debug(string, ...)
48   -
49   -#endif
50   -
51 43 /* A NULL resistant strlen. Unlike it's libc sibling, l_strlen returns a
52 44 * ssize_t, and supports its argument being NULL. */
53 45 static inline ssize_t l_strlen(const gchar *s) {
@@ -59,4 +51,4 @@ static inline ssize_t l_strlen(const gchar *s) {
59 51 gboolean file_exists(const gchar*);
60 52
61 53 #endif
62   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  54 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  config.mk
@@ -14,7 +14,7 @@ INCS := $(shell pkg-config --cflags ${PKGS}) -I./
14 14 LIBS := $(shell pkg-config --libs ${PKGS})
15 15
16 16 # Add flags
17   -CPPFLAGS := -DVERSION=\"${VERSION}\" -DDEBUG_MESSAGES ${CPPFLAGS}
  17 +CPPFLAGS := -DVERSION=\"${VERSION}\" ${CPPFLAGS}
18 18 CFLAGS := -std=c99 -ggdb -W -Wall -Wextra ${INCS} ${CFLAGS}
19 19 LDFLAGS := ${LIBS} ${LDFLAGS}
20 20
4 globalconf.h.in
@@ -21,7 +21,7 @@
21 21 #ifndef LUAKIT_GLOBALCONF
22 22 #define LUAKIT_GLOBALCONF
23 23
24   -#define LUAKIT_LUA_LIB_PATH "/usr/local/share/luakit/lib"
  24 +#define LUAKIT_INSTALL_PATH "/usr/local/share/luakit"
25 25 #define LUAKIT_OBJECT_REGISTRY_KEY "luakit.object.registry"
26 26
27 27 #include <glib/gtypes.h>
@@ -35,6 +35,8 @@ typedef struct {
35 35 gchar *base_data_directory;
36 36 /* Path to the current config file */
37 37 gchar *confpath;
  38 + /* Print verbose output */
  39 + gboolean verbose;
38 40 /* Lua VM state */
39 41 lua_State *L;
40 42 /* Global signals table */
2  lib/bind.lua
@@ -120,3 +120,5 @@ function hit(binds, mods, key, buffer, enable_buffer, arg)
120 120 end
121 121 return true
122 122 end
  123 +
  124 +-- vim: ft=lua:et:sw=4:ts=8:sts=4:tw=80
2  lib/mode.lua
@@ -56,3 +56,5 @@ function set_default(win, mode)
56 56 end
57 57
58 58 setmetatable(_M, { __call = function(_, ...) return set(...) end })
  59 +
  60 +-- vim: ft=lua:et:sw=4:ts=8:sts=4:tw=80
41 lib/util.lua
@@ -15,6 +15,8 @@ local rtable = table
15 15 local string = string
16 16 local type = type
17 17 local print = print
  18 +local error = error
  19 +local capi = { luakit = luakit }
18 20
19 21 -- Utility module for awful
20 22 module("util")
@@ -139,4 +141,41 @@ function table.pop(t, k)
139 141 return v
140 142 end
141 143
142   --- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=80
  144 +-- Check if a file exists
  145 +function os.exists(f)
  146 + fh, err = io.open(f)
  147 + if fh then
  148 + fh:close()
  149 + return true
  150 + end
  151 +end
  152 +
  153 +-- Table of xdg home paths or their default locations
  154 +xdg = {
  155 + config_home = (os.getenv("XDG_CONFIG_HOME") or (os.getenv("HOME") .. "/.config")) .. "/luakit",
  156 + data_home = (os.getenv("XDG_DATA_HOME") or (os.getenv("HOME") .. "/.local/share")) .. "/luakit",
  157 + cache_home = (os.getenv("XDG_CACHE_HOME") or (os.getenv("HOME") .. "/.cache")) .. "/luakit",
  158 +}
  159 +
  160 +-- Search locally, xdg home path and then luakit install path for a given file
  161 +function xdg_find(f, xdg_home_path)
  162 + -- Ignore absolute paths
  163 + if string.match(f, "^/") then
  164 + if os.exists(f) then return f end
  165 + error(string.format("xdg_find: No such file: %s\n", f))
  166 + end
  167 +
  168 + -- Check if file exists at the following locations & return first match
  169 + local paths = { f, xdg_home_path .. "/" .. f, capi.luakit.install_path .. "/" .. f }
  170 + for _, p in ipairs(paths) do
  171 + if os.exists(p) then return p end
  172 + end
  173 +
  174 + error(string.format("xdg_find: No such file at:\n\t%s\n", rtable.concat(paths, ",\n\t")))
  175 +end
  176 +
  177 +function find_config(f) return xdg_find(f, xdg.config_home) end
  178 +function find_data(f) return xdg_find(f, xdg.data_home) end
  179 +function find_cache(f) return xdg_find(f, xdg.cache_home) end
  180 +
  181 +-- vim: ft=lua:et:sw=4:ts=8:sts=4:tw=80
48 luah.c
@@ -311,6 +311,40 @@ luaH_isloop(lua_State *L, gint idx)
311 311 return !ret;
312 312 }
313 313
  314 +/* Returns a string from X selection.
  315 + * \param L The Lua VM state.
  316 + * \return The number of elements pushed on stack (1).
  317 + */
  318 +static gint
  319 +luaH_luakit_selection(lua_State *L)
  320 +{
  321 + int n = lua_gettop(L);
  322 + GdkAtom atom = GDK_SELECTION_PRIMARY;
  323 +
  324 + if (n) {
  325 + const gchar *arg = luaL_checkstring(L, 1);
  326 + /* Follow xclip(1) behavior: check only the first character of argument */
  327 + switch (arg[0]) {
  328 + case 'p':
  329 + break;
  330 + case 's':
  331 + atom = GDK_SELECTION_SECONDARY;
  332 + break;
  333 + case 'c':
  334 + atom = GDK_SELECTION_CLIPBOARD;
  335 + break;
  336 + default:
  337 + luaL_argerror(L, 1, "should be 'primary', 'secondary' or 'clipboard'");
  338 + break;
  339 + }
  340 + }
  341 + GtkClipboard *selection = gtk_clipboard_get(atom);
  342 + gchar *text = gtk_clipboard_wait_for_text(selection);
  343 + lua_pushstring(L, text);
  344 + g_free(text);
  345 + return 1;
  346 +}
  347 +
314 348 /* luakit global table.
315 349 * \param L The Lua VM state.
316 350 * \return The number of elements pushed on stack.
@@ -341,6 +375,14 @@ luaH_luakit_index(lua_State *L)
341 375 }
342 376 return 1;
343 377
  378 + case L_TK_SELECTION:
  379 + lua_pushcfunction(L, luaH_luakit_selection);
  380 + return 1;
  381 +
  382 + case L_TK_INSTALL_PATH:
  383 + lua_pushstring(L, LUAKIT_INSTALL_PATH);
  384 + return 1;
  385 +
344 386 default:
345 387 break;
346 388 }
@@ -522,8 +564,8 @@ luaH_init(xdgHandle *xdg)
522 564 }
523 565
524 566 /* add Lua lib path (/usr/share/luakit/lib by default) */
525   - lua_pushliteral(L, ";" LUAKIT_LUA_LIB_PATH "/?.lua");
526   - lua_pushliteral(L, ";" LUAKIT_LUA_LIB_PATH "/?/init.lua");
  567 + lua_pushliteral(L, ";" LUAKIT_INSTALL_PATH "/lib/?.lua");
  568 + lua_pushliteral(L, ";" LUAKIT_INSTALL_PATH "/lib/?/init.lua");
527 569 lua_concat(L, 3); /* concatenate with package.path */
528 570 lua_setfield(L, 1, "path"); /* package.path = "concatenated string" */
529 571 }
@@ -603,4 +645,4 @@ luaH_class_newindex_miss_property(lua_State *L, lua_object_t *obj)
603 645 return 0;
604 646 }
605 647
606   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  648 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  luah.h
@@ -157,4 +157,4 @@ void luaH_modifier_table_push(lua_State *, guint);
157 157 void luaH_keystr_push(lua_State *, guint);
158 158
159 159 #endif
160   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  160 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
25 luakit.c
@@ -2,6 +2,7 @@
2 2 * luakit.c - luakit main functions
3 3 *
4 4 * Copyright (C) 2010 Mason Larobina <mason.larobina@gmail.com>
  5 + * Copyright (C) 2009 Enno Boland <gottox@s01.de>
5 6 *
6 7 * This program is free software: you can redistribute it and/or modify
7 8 * it under the terms of the GNU General Public License as published by
@@ -18,15 +19,6 @@
18 19 *
19 20 */
20 21
21   -/*
22   - * MIT/X Consortium License (applies to some functions from surf.c)
23   - *
24   - * (C) 2009 Enno Boland <gottox@s01.de>
25   - *
26   - * See COPYING.MIT for the full license.
27   - *
28   - */
29   -
30 22 #include <gtk/gtk.h>
31 23 #include <signal.h>
32 24 #include <stdlib.h>
@@ -55,13 +47,12 @@ parseopts(int argc, char *argv[]) {
55 47
56 48 /* define command line options */
57 49 const GOptionEntry entries[] = {
58   - { "uri", 'u', 0, G_OPTION_ARG_STRING_ARRAY, &uris,
59   - "uri(s) to load at startup", "URI" },
60   - { "config", 'c', 0, G_OPTION_ARG_STRING, &globalconf.confpath,
61   - "configuration file to use", "FILE" },
62   - { "version", 'V', 0, G_OPTION_ARG_NONE, &only_version,
63   - "show version", NULL },
64   - { NULL, 0, 0, 0, NULL, NULL, NULL }};
  50 + { "uri", 'u', 0, G_OPTION_ARG_STRING_ARRAY, &uris, "uri(s) to load at startup", "URI" },
  51 + { "config", 'c', 0, G_OPTION_ARG_STRING, &globalconf.confpath, "configuration file to use", "FILE" },
  52 + { "version", 'V', 0, G_OPTION_ARG_NONE, &only_version, "print version and exit", NULL },
  53 + { "verbose", 'v', 0, G_OPTION_ARG_NONE, &globalconf.verbose, "print debugging output", NULL },
  54 + { NULL, 0, 0, 0, NULL, NULL, NULL },
  55 + };
65 56
66 57 /* parse command line options */
67 58 context = g_option_context_new("[URI...]");
@@ -157,4 +148,4 @@ main(int argc, char *argv[]) {
157 148 return EXIT_SUCCESS;
158 149 }
159 150
160   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  151 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  luakit.h
@@ -37,4 +37,4 @@ typedef struct {
37 37 extern Luakit luakit;
38 38
39 39 #endif
40   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  40 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
340 rc.lua
@@ -17,8 +17,14 @@ function entry() return widget{type="entry"} end
17 17 -- Variable definitions
18 18 HOMEPAGE = "http://luakit.org/"
19 19 --HOMEPAGE = "http://github.com/mason-larobina/luakit"
20   -SCROLL_STEP = 20
21   -MAX_HISTORY = 100
  20 +SCROLL_STEP = 20
  21 +MAX_CMD_HISTORY = 100
  22 +MAX_SRCH_HISTORY = 100
  23 +--HTTPPROXY = "http://example.com:3128"
  24 +
  25 +-- Setup download directory
  26 +DOWNLOAD_DIR = (os.getenv("HOME") or ".") .. "/downloads"
  27 +os.execute(string.format("mkdir -p %q", DOWNLOAD_DIR))
22 28
23 29 -- Luakit theme
24 30 theme = theme or {
@@ -56,6 +62,13 @@ widget.add_signal("new", function(wi)
56 62 end)
57 63 end)
58 64
  65 +-- Search engines
  66 +search_engines = {
  67 + google = "http://google.com/search?q={0}",
  68 + imdb = "http://imdb.com/find?s=all&q={0}",
  69 + sourceforge = "http://sf.net/search/?words={0}"
  70 +}
  71 +
59 72 -- Add key bindings to be used across all windows
60 73 mode_binds = {
61 74 -- bind.buf(Pattern, function (w, buffer, opts) .. end, opts),
@@ -81,6 +94,15 @@ mode_binds = {
81 94 bind.buf("^G$", function (w) w:scroll_vert("100%") end),
82 95 bind.buf("^[\-\+]?[0-9]+[%%G]$", function (w, b) w:scroll_vert(string.match(b, "^([\-\+]?%d+)[%%G]$") .. "%") end),
83 96
  97 + -- Clipboard
  98 + bind.key({}, "p", function (w) w:navigate(luakit.selection()) end),
  99 + bind.key({}, "P", function (w) w:new_tab(luakit.selection()) end),
  100 +
  101 + -- Commands
  102 + bind.buf("^o$", function (w, c) w:enter_cmd(":open ") end),
  103 + bind.buf("^t$", function (w, c) w:enter_cmd(":tabopen ") end),
  104 + bind.buf("^,g$", function (w, c) w:enter_cmd(":websearch google ") end),
  105 +
84 106 -- Searching
85 107 bind.key({}, "/", function (w) w:start_search(true) end),
86 108 bind.key({}, "?", function (w) w:start_search(false) end),
@@ -95,13 +117,25 @@ mode_binds = {
95 117 bind.buf("^[0-9]*gT$", function (w, b) w:prev_tab(tonumber(string.match(b, "^(%d*)gT$") or 1)) end),
96 118 bind.buf("^[0-9]*gt$", function (w, b) w:next_tab(tonumber(string.match(b, "^(%d*)gt$") or 1)) end),
97 119 bind.buf("^gH$", function (w) w:new_tab(HOMEPAGE) end),
  120 + bind.buf("^d$", function (w) w:close_tab() end),
98 121
99 122 bind.buf("^gh$", function (w) w:navigate(HOMEPAGE) end),
100 123 bind.buf("^ZZ$", function (w) luakit.quit() end),
  124 +
  125 + -- Link following
  126 + bind.key({}, "f", function (w) w:set_mode("follow") end),
101 127 },
102 128 command = {
  129 + bind.key({"Shift"}, "Insert", function (w) w:insert_cmd(luakit.selection()) end),
103 130 bind.key({}, "Up", function (w) w:cmd_hist_prev() end),
104 131 bind.key({}, "Down", function (w) w:cmd_hist_next() end),
  132 + bind.key({}, "Tab", function (w) w:cmd_completion() end),
  133 + bind.key({"Control"}, "w", function (w) w:del_word() end),
  134 + bind.key({"Control"}, "u", function (w) w:del_line() end),
  135 + },
  136 + search = {
  137 + bind.key({}, "Up", function (w) w:srch_hist_prev() end),
  138 + bind.key({}, "Down", function (w) w:srch_hist_next() end),
105 139 },
106 140 insert = { },
107 141 }
@@ -115,8 +149,19 @@ commands = {
115 149 bind.cmd({"forward", "f"}, function (w, a) w:forward(tonumber(a) or 1) end),
116 150 bind.cmd({"scroll" }, function (w, a) w:scroll_vert(a) end),
117 151 bind.cmd({"quit", "q"}, function (w) luakit.quit() end),
  152 + bind.cmd({"close", "c"}, function (w) w:close_tab() end),
  153 + bind.cmd({"websearch", "ws"}, function (w, e, s) w:websearch(e, s) end),
118 154 }
119 155
  156 +function set_http_options(w)
  157 + local proxy = HTTPPROXY or os.getenv("http_proxy")
  158 + if proxy then w:set('proxy-uri', proxy) end
  159 + w:set('user-agent', 'luakit')
  160 + -- Uncomment the following options if you want to enable SSL certs validation.
  161 + -- w:set('ssl-ca-file', '/etc/certs/ca-certificates.crt')
  162 + -- w:set('ssl-strict', true)
  163 +end
  164 +
120 165 -- Build and pack window widgets
121 166 function build_window()
122 167 -- Create a table for widgets and state variables for a window
@@ -228,15 +273,29 @@ function attach_window_signals(w)
228 273
229 274 -- Attach window widget signals
230 275 w.win:add_signal("key-press", function(win, mods, key)
  276 + -- Reset command line completion
  277 + if w:get_mode() == "command" and key ~= "Tab" and w.compl_start then
  278 + w:update_uri()
  279 + w.compl_index = 0
  280 + end
  281 +
231 282 if w:hit(mods, key) then
232 283 return true
233 284 end
234 285 end)
235 286
236 287 w.win:add_signal("mode-changed", function(win, mode)
  288 + local i, p = w.ibar.input, w.ibar.prompt
  289 +
237 290 w:update_binds(mode)
238 291 w.cmd_hist_cursor = nil
239 292
  293 + -- Clear following hints if the user exits follow mode
  294 + if w.showing_hints then
  295 + w:eval_js("clear();");
  296 + w.showing_hints = false
  297 + end
  298 +
240 299 -- If a user aborts a search return to the original position
241 300 if w.search_start_marker then
242 301 w:get_current():set_scroll_vert(w.search_start_marker)
@@ -244,22 +303,32 @@ function attach_window_signals(w)
244 303 end
245 304
246 305 if mode == "normal" then
247   - w.ibar.prompt:hide()
248   - w.ibar.input:hide()
  306 + p:hide()
  307 + i:hide()
249 308 elseif mode == "insert" then
250   - w.ibar.input:hide()
251   - w.ibar.input.text = ""
252   - w.ibar.prompt.text = "-- INSERT --"
253   - w.ibar.prompt:show()
  309 + i:hide()
  310 + i.text = ""
  311 + p.text = "-- INSERT --"
  312 + p:show()
254 313 elseif mode == "command" then
255   - w.ibar.prompt:hide()
256   - w.ibar.input.text = ":"
257   - w.ibar.input:show()
258   - w.ibar.input:focus()
259   - w.ibar.input:set_position(-1)
  314 + p:hide()
  315 + i.text = ":"
  316 + i:show()
  317 + i:focus()
  318 + i:set_position(-1)
260 319 elseif mode == "search" then
261   - w.ibar.prompt:hide()
262   - w.ibar.input:show()
  320 + p:hide()
  321 + i:show()
  322 + elseif mode == "follow" then
  323 + w:eval_js_from_file(util.find_data("scripts/follow.js"))
  324 + w:eval_js("clear(); show_hints();")
  325 + w.showing_hints = true
  326 + p.text = "Follow:"
  327 + p:show()
  328 + i.text = ""
  329 + i:show()
  330 + i:focus()
  331 + i:set_position(-1)
263 332 else
264 333 w.ibar.prompt.text = ""
265 334 w.ibar.input.text = ""
@@ -280,6 +349,8 @@ function attach_window_signals(w)
280 349 w:clear_search()
281 350 w:set_mode()
282 351 end
  352 + elseif w:is_mode("follow") then
  353 + w:eval_js(string.format("update(%q)", w.ibar.input.text))
283 354 end
284 355 end)
285 356
@@ -290,12 +361,12 @@ function attach_window_signals(w)
290 361 w:match_cmd(string.sub(text, 2))
291 362 w:set_mode()
292 363 elseif w:is_mode("search") then
293   - -- TODO add search term to some history list
294   - w:search(string.sub(text, 2), (string.sub(text, 1, 1) == "/"))
  364 + w:srch_hist_add(text)
  365 + w:search(string.sub(text, 2), string.sub(text, 1, 1) == "/")
295 366 -- User doesn't want to return to start position
296 367 w.search_start_marker = nil
297 368 w:set_mode()
298   - w.ibar.prompt.text = text
  369 + w.ibar.prompt.text = util.escape(text)
299 370 w.ibar.prompt:show()
300 371 end
301 372 end)
@@ -317,6 +388,18 @@ function attach_webview_signals(w, view)
317 388 end
318 389 end)
319 390
  391 + view:add_signal("link-hover", function (v, link)
  392 + if w:is_current(v) and link then
  393 + w.sbar.l.uri.text = "Link: " .. util.escape(link)
  394 + end
  395 + end)
  396 +
  397 + view:add_signal("link-unhover", function (v)
  398 + if w:is_current(v) then
  399 + w:update_uri(v)
  400 + end
  401 + end)
  402 +
320 403 view:add_signal("key-press", function ()
321 404 -- Only allow key press events to hit the webview if the user is in
322 405 -- "insert" mode.
@@ -332,6 +415,35 @@ function attach_webview_signals(w, view)
332 415 end
333 416 end)
334 417
  418 + -- 'link' contains the download link
  419 + -- 'mime' contains the mime type that is requested
  420 + -- return TRUE to accept or FALSE to reject
  421 + view:add_signal("mime-type-decision", function (v, link, mime)
  422 + if w:is_current(v) then
  423 + print(string.format("Requested link: %s (%s)", link, mime))
  424 +
  425 + -- i.e. block binary files like *.exe
  426 + if string.match(mime, "application/octet-stream") then
  427 + return false
  428 + end
  429 + end
  430 + end)
  431 +
  432 + -- 'link' contains the download link
  433 + -- 'filename' contains the suggested filename (from server or webkit)
  434 + view:add_signal("download-request", function (v, link, filename)
  435 + if w:is_current(v) and filename then
  436 + local dl = DOWNLOAD_DIR .. "/" .. filename
  437 +
  438 + print ("Download request:", link)
  439 + print ("Suggested filename:", filename)
  440 +
  441 + local wget = string.format("wget -q %q -O %q &", link, dl)
  442 + print("Launching: " .. wget)
  443 + os.execute(wget)
  444 + end
  445 + end)
  446 +
335 447 view:add_signal("progress-update", function (v)
336 448 if w:is_current(v) then
337 449 w:update_progress(v)
@@ -393,18 +505,48 @@ window_helpers = {
393 505 end,
394 506
395 507 navigate = function(w, uri, view)
396   - (view or w:get_current()).uri = uri
  508 + local v = view or w:get_current()
  509 + if v then
  510 + v.uri = uri
  511 + else
  512 + return w:new_tab(uri)
  513 + end
397 514 end,
398 515
399 516 new_tab = function(w, uri)
400 517 local view = webview()
401 518 w.tabs:append(view)
  519 + set_http_options(w)
402 520 attach_webview_signals(w, view)
403 521 if uri then view.uri = uri end
404 522 view.show_scrollbars = false
405 523 w:update_tab_count()
406 524 end,
407 525
  526 + -- close the current tab
  527 + close_tab = function(w)
  528 + view = w:get_current()
  529 + if not view then return end
  530 + w.tabs:remove(view)
  531 + view:destroy()
  532 + w:update_tab_count()
  533 + end,
  534 +
  535 + -- evaluate javascript code and return string result
  536 + eval_js = function(w, script, file, view)
  537 + if not view then view = w:get_current() end
  538 + return view:eval_js(script, file or "(buffer)")
  539 + end,
  540 +
  541 + -- evaluate javascript code from file and return string result
  542 + eval_js_from_file = function(w, file, view)
  543 + local fh, err = io.open(file)
  544 + if not fh then return error(err) end
  545 + local script = fh:read("*a")
  546 + fh:close()
  547 + return w:eval_js(script, file, view)
  548 + end,
  549 +
408 550 -- Wrapper around the bind plugin's hit method
409 551 hit = function (w, mods, key)
410 552 local caught, newbuf = bind.hit(w.binds or {}, mods, key, w.buffer, w:is_mode("normal"), w)
@@ -418,12 +560,157 @@ window_helpers = {
418 560 return bind.match_cmd(commands, buffer, w)
419 561 end,
420 562
  563 + -- enter command or characters into command line
  564 + enter_cmd = function(w, cmd)
  565 + local i = w.ibar.input
  566 + w:set_mode("command")
  567 + i.text = cmd
  568 + i:set_position(-1)
  569 + end,
  570 +
  571 + -- insert a string into the command line at the current cursor position
  572 + insert_cmd = function(w, str)
  573 + if not str then return nil end
  574 + local i = w.ibar.input
  575 + local text = i.text
  576 + local pos = i:get_position()
  577 + local left, right = string.sub(text, 1, pos), string.sub(text, pos+1)
  578 + i.text = left .. str .. right
  579 + i:set_position(pos + #str + 1)
  580 + end,
  581 +
  582 + -- search engine wrapper
  583 + websearch = function(w, args)
  584 + local sep = string.find(args, " ")
  585 + local engine = string.sub(args, 1, sep-1)
  586 + local search = string.sub(args, sep+1)
  587 + if not search_engines[engine] then
  588 + print("E: No matching search engine found:", engine)
  589 + return 0
  590 + end
  591 + local uri = string.gsub(search_engines[engine], "{%d}", search)
  592 + return w:navigate(uri)
  593 + end,
  594 +
  595 + -- Command line completion of available commands
  596 + cmd_completion = function(w)
  597 + local i = w.ibar.input
  598 + local s = w.sbar.l.uri
  599 + local cmpl = {}
  600 +
  601 + -- Get last completion (is reset on key press other than <Tab>)
  602 + if not w.compl_start or w.compl_index == 0 then
  603 + w.compl_start = "^" .. string.sub(i.text, 2)
  604 + w.compl_index = 1
  605 + end
  606 +
  607 + -- Get suitable commands
  608 + for _, b in ipairs(commands) do
  609 + for _, c in pairs(b.commands) do
  610 + if c and string.match(c, w.compl_start) then
  611 + table.insert(cmpl, c)
  612 + end
  613 + end
  614 + end
  615 +
  616 + table.sort(cmpl)
  617 +
  618 + if #cmpl > 0 then
  619 + local text = ""
  620 + for index, comp in pairs(cmpl) do
  621 + if index == w.compl_index then
  622 + i.text = ":" .. comp .. " "
  623 + i:set_position(-1)
  624 + end
  625 + if text ~= "" then
  626 + text = text .. " | "
  627 + end
  628 + text = text .. comp
  629 + end
  630 +
  631 + -- cycle through all possible completions
  632 + if w.compl_index == #cmpl then
  633 + w.compl_index = 1
  634 + else
  635 + w.compl_index = w.compl_index + 1
  636 + end
  637 + s.text = util.escape(text)
  638 + end
  639 + end,
  640 +
  641 + del_word = function(w)
  642 + local i = w.ibar.input
  643 + local text = i.text
  644 + local pos = i:get_position()
  645 + if text and #text > 1 and pos > 1 then
  646 + local left, right = string.sub(text, 2, pos), string.sub(text, pos+1)
  647 + if not string.find(left, "%s") then
  648 + left = ""
  649 + elseif string.find(left, "%w+%s*$") then
  650 + left = string.sub(left, 0, string.find(left, "%w+%s*$") - 1)
  651 + elseif string.find(left, "%W+%s*$") then
  652 + left = string.sub(left, 0, string.find(left, "%W+%s*$") - 1)
  653 + end
  654 + i.text = string.sub(text, 1, 1) .. left .. right
  655 + i:set_position(#left + 2)
  656 + end
  657 + end,
  658 +
  659 + del_line = function(w)
  660 + local i = w.ibar.input
  661 + if i.text ~= ":" then
  662 + i.text = ":"
  663 + i:set_position(-1)
  664 + end
  665 + end,
  666 +
  667 + -- Search history adding
  668 + srch_hist_add = function(w, srch)
  669 + if not w.srch_hist then w.srch_hist = {} end
  670 + -- Check overflow
  671 + if #w.srch_hist > ((MAX_SRCH_HISTORY or 100) + 5) then
  672 + while #w.srch_hist > (MAX_SRCH_HISTORY or 100) do
  673 + table.remove(w.srch_hist, 1)
  674 + end
  675 + end
  676 + table.insert(w.srch_hist, srch)
  677 + end,
  678 +
  679 + -- Search history traversing
  680 + srch_hist_prev = function(w)
  681 + if not w.srch_hist then w.srch_hist = {} end
  682 + if not w.srch_hist_cursor then
  683 + w.srch_hist_cursor = #w.srch_hist + 1
  684 + w.srch_hist_current = w.ibar.input.text
  685 + end
  686 + local c = w.srch_hist_cursor - 1
  687 + if w.srch_hist[c] then
  688 + w.srch_hist_cursor = c
  689 + w.ibar.input.text = w.srch_hist[c]
  690 + w.ibar.input:set_position(-1)
  691 + end
  692 + end,
  693 +
  694 + srch_hist_next = function(w)
  695 + if not w.srch_hist then w.srch_hist = {} end
  696 + local c = (w.srch_hist_cursor or #w.srch_hist) + 1
  697 + if w.srch_hist[c] then
  698 + w.srch_hist_cursor = c
  699 + w.ibar.input.text = w.srch_hist[c]
  700 + w.ibar.input:set_position(-1)
  701 + elseif w.srch_hist_current then
  702 + w.srch_hist_cursor = nil
  703 + w.ibar.input.text = w.srch_hist_current
  704 + w.ibar.input:set_position(-1)
  705 + end
  706 + end,
  707 +
421 708 -- Command history adding
422 709 cmd_hist_add = function(w, cmd)
423 710 if not w.cmd_hist then w.cmd_hist = {} end
424 711 -- Make sure history doesn't overflow
425   - if #w.cmd_hist > ((MAX_HISTORY or 100) + 5) then
426   - while #w.cmd_hist > (MAX_HISTORY or 100) do
  712 + if #w.cmd_hist > ((MAX_CMD_HISTORY or 100) + 5) then
  713 + while #w.cmd_hist > (MAX_CMD_HISTORY or 100) do
427 714 table.remove(w.cmd_hist, 1)
428 715 end
429 716 end
@@ -561,8 +848,9 @@ window_helpers = {
561 848 end,
562 849
563 850 update_uri = function (w, view, uri)
564   - if not view then view = w:get_current() end
565   - w.sbar.l.uri.text = (uri or view.uri or "about:blank")
  851 + local v = view or w:get_current()
  852 + if not v then return end
  853 + w.sbar.l.uri.text = util.escape((uri or v.uri or "about:blank"))
566 854 end,
567 855
568 856 update_progress = function (w, view, p)
@@ -589,7 +877,7 @@ window_helpers = {
589 877
590 878 update_buf = function (w)
591 879 if w.buffer then
592   - w.sbar.r.buf.text = string.format(" %-3s", w.buffer)
  880 + w.sbar.r.buf.text = util.escape(string.format(" %-3s", w.buffer))
593 881 w.sbar.r.buf:show()
594 882 else
595 883 w.sbar.r.buf:hide()
@@ -654,7 +942,7 @@ window_helpers = {
654 942 for i = 1, count do
655 943 local t = tb.titles[i]
656 944 local title = " " ..i.. " "..w:get_tab_title(w.tabs:atindex(i))
657   - t.label.text = string.format(theme.tablabel_format or "%s", title)
  945 + t.label.text = util.escape(string.format(theme.tablabel_format or "%s", title))
658 946 w:apply_tablabel_theme(t, i == current)
659 947 end
660 948 end
@@ -740,3 +1028,5 @@ function new_window(uris)
740 1028 end
741 1029
742 1030 new_window(uris)
  1031 +
  1032 +-- vim: ft=lua:et:sw=4:ts=8:sts=4:tw=80
246 scripts/follow.js
... ... @@ -0,0 +1,246 @@
  1 +var elements = [];
  2 +var active_arr = [];
  3 +var hints;
  4 +var overlays;
  5 +var active;
  6 +var lastpos = 0;
  7 +var last_input = "";
  8 +var last_strings = [];
  9 +
  10 +focus_color = "#00ff00";
  11 +normal_color = "#ffff99";
  12 +opacity = 0.3;
  13 +border = "1px dotted #000000";
  14 +
  15 +hint_foreground = "#ffffff";
  16 +hint_background = "#000088";
  17 +hint_border = "2px dashed #000000";
  18 +hint_opacity = 0.4;
  19 +hint_font = "11px monospace bold";
  20 +
  21 +vertical_offset = 0;
  22 +horizontal_offset = -10;
  23 +
  24 +function Hint(element) {
  25 + this.element = element;
  26 + this.rect = element.getBoundingClientRect();
  27 +
  28 + function create_span(element, h, v) {
  29 + var span = document.createElement("span");
  30 + var leftpos = Math.max((element.rect.left + document.defaultView.scrollX), document.defaultView.scrollX) + h;
  31 + var toppos = Math.max((element.rect.top + document.defaultView.scrollY), document.defaultView.scrollY) + v;
  32 + span.style.position = "absolute";
  33 + span.style.left = leftpos + "px";
  34 + span.style.top = toppos + "px";
  35 + return span;
  36 + }
  37 + function create_hint(element) {
  38 + var hint = create_span(element, horizontal_offset, vertical_offset - element.rect.height/2);
  39 + hint.style.font = hint_font;
  40 + hint.style.color = hint_foreground;
  41 + hint.style.background = hint_background;
  42 + hint.style.opacity = hint_opacity;
  43 + hint.style.border = hint_border;
  44 + hint.style.zIndex = 10001;
  45 + hint.style.visibility = 'visible';
  46 + return hint;
  47 + }
  48 + function create_overlay(element) {
  49 + var overlay = create_span(element, 0, 0);
  50 + overlay.style.width = element.rect.width + "px";
  51 + overlay.style.height = element.rect.height + "px";
  52 + overlay.style.opacity = opacity;
  53 + overlay.style.backgroundColor = normal_color;
  54 + overlay.style.border = border;
  55 + overlay.style.zIndex = 10000;
  56 + overlay.style.visibility = 'visible';
  57 + overlay.addEventListener( 'click', function() { click_element(element); }, false );
  58 + return overlay;
  59 + }
  60 +
  61 + this.hint = create_hint(this);
  62 + this.overlay = create_overlay(this);
  63 +}
  64 +function reload_hints(array, input, keep) {
  65 + var length = array.length;
  66 + var start = length < 10 ? 1 : length < 100 ? 10 : 100;
  67 + var bestposition = 37;
  68 +
  69 + for (var i=0; i<length; i++) {
  70 + var e = array[i];
  71 + e.overlay.style.backgroundColor = normal_color;
  72 + if (!e.hint.parentNode && !e.hint.firstchild) {
  73 + var content = document.createTextNode(start + i);
  74 + e.hint.appendChild(content);
  75 + hints.appendChild(e.hint);
  76 + }
  77 + else if (!keep) {
  78 + e.hint.textContent = start + i;
  79 + }
  80 + if (!e.overlay.parentNode && !e.overlay.firstchild) {
  81 + overlays.appendChild(e.overlay);
  82 + }
  83 + if (input && bestposition != 0) {
  84 + // match word beginnings
  85 + var content = e.element.textContent.toLowerCase().split(" ");
  86 + for (var cl=0; cl<content.length; cl++) {
  87 + if (content[cl].toLowerCase().indexOf(input) == 0) {
  88 + if (cl < bestposition) {
  89 + lastpos = i;
  90 + bestposition = cl;
  91 + break;
  92 + }
  93 + }
  94 + }
  95 + }
  96 + }
  97 + active = array[lastpos];
  98 + active.overlay.style.backgroundColor = focus_color;
  99 +}
  100 +function click_element(e) {
  101 + var mouseEvent = document.createEvent("MouseEvent");
  102 + mouseEvent.initMouseEvent("click", true, true, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  103 + e.element.dispatchEvent(mouseEvent);
  104 + clear();
  105 +}
  106 +function show_hints() {
  107 + document.activeElement.blur();
  108 + if ( elements ) {
  109 + var res = document.body.querySelectorAll('a, area, textarea, select, link, input:not([type=hidden]), button, frame, iframe');
  110 + hints = document.createElement("div");
  111 + overlays = document.createElement("div");
  112 + for (var i=0; i<res.length; i++) {
  113 + var e = new Hint(res[i]);
  114 + var rects = e.element.getClientRects()[0];
  115 + var r = e.rect;
  116 + if (!r || r.top > window.innerHeight || r.bottom < 0 || r.left > window.innerWidth || r < 0 || !rects ) {
  117 + continue;
  118 + }
  119 + var style = document.defaultView.getComputedStyle(e.element, null);
  120 + if (style.getPropertyValue("visibility") != "visible" || style.getPropertyValue("display") == "none") {
  121 + continue;
  122 + }
  123 + elements.push(e);
  124 + };
  125 + elements.sort( function(a,b) { return a.rect.top - b.rect.top; });
  126 + active_arr = elements;
  127 + reload_hints(elements);
  128 + document.body.appendChild(hints);
  129 + document.body.appendChild(overlays);
  130 + }
  131 +}
  132 +function is_input(element) {
  133 + var e = element.element;
  134 + var type = e.type.toLowerCase();
  135 + if (e.tagName == "INPUT" || e.tagName == "TEXTAREA" ) {
  136 + if (type == "radio" || type == "checkbox") {
  137 + e.checked = !e.checked;
  138 + }
  139 + else if (type == "submit" || type == "reset" || type == "button") {
  140 + click_element(element);
  141 + }
  142 + else {
  143 + e.focus();
  144 + }
  145 + return true;
  146 + }
  147 + return false;
  148 +}
  149 +function update_hints(input) {
  150 + var array = [];
  151 + var text_content;
  152 + var keep = false;
  153 + if (input) {
  154 + input = input.toLowerCase();
  155 + }
  156 + for (var i=0; i<active_arr.length; i++) {
  157 + var e = active_arr[i];
  158 + if (parseInt(input) == input) {
  159 + text_content = e.hint.textContent;
  160 + keep = true;
  161 + }
  162 + else {
  163 + text_content = e.element.textContent.toLowerCase();
  164 + }
  165 + if (text_content.match(input)) {
  166 + array.push(e);
  167 + }
  168 + else {
  169 + e.hint.style.visibility = 'hidden';
  170 + e.overlay.style.visibility = 'hidden';
  171 + }
  172 + }
  173 + active_arr = array;
  174 + if (array.length == 0) {
  175 + clear();
  176 + return;
  177 + }
  178 + if (array.length == 1) {
  179 + if (evaluate(array[0])) {
  180 + return "__HINT_EVALUATED__"
  181 + }
  182 + }
  183 + reload_hints(array, input, keep);
  184 +}
  185 +function clear() {
  186 + if (overlays && overlays.parentNode) {
  187 + overlays.parentNode.removeChild(overlays);
  188 + }
  189 + if (hints && hints.parentNode) {
  190 + hints.parentNode.removeChild(hints);
  191 + }
  192 + elements = [];
  193 + active_arr = [];
  194 + active = undefined;
  195 +}
  196 +function evaluate(element) {
  197 + var ret = false;
  198 + var e = element.element;
  199 + if (!is_input(element) && e.href) {
  200 + if (e.href.match(/javascript:/) || (e.type.toLowerCase() == "button")) {
  201 + click_element(element);
  202 + ret = true;
  203 + }
  204 + else {
  205 + document.location = e.href;
  206 + ret = true;
  207 + }
  208 + }
  209 + clear();
  210 + return ret;
  211 +}
  212 +function get_active() {
  213 + return evaluate(active);
  214 +}
  215 +function focus(newpos) {
  216 + active_arr[lastpos].overlay.style.backgroundColor = normal_color;
  217 + active_arr[newpos].overlay.style.backgroundColor = focus_color;
  218 + active = active_arr[newpos];
  219 + lastpos = newpos;
  220 +}
  221 +function focus_next() {
  222 + var newpos = lastpos == active_arr.length-1 ? 0 : lastpos + 1;
  223 + focus(newpos);
  224 +}
  225 +function focus_prev() {
  226 + var newpos = lastpos == 0 ? active_arr.length-1 : lastpos - 1;
  227 + focus(newpos);
  228 +}
  229 +
  230 +function update(input) {
  231 + input = input.replace(/(\d+)$/, " $1");
  232 + strings = input.split(" ");
  233 + if (input.length < last_input.length || strings.length < last_strings.length) {
  234 + // user removed a char
  235 + clear();
  236 + show_hints();
  237 + for (var i = 0; i < strings.length; i += 1) {
  238 + update_hints(strings[i]);
  239 + }
  240 + } else {
  241 + update_hints(strings[strings.length-1]);
  242 + }
  243 + last_input = input;
  244 + last_strings = strings;
  245 +}
  246 +
2  widget.c
@@ -179,4 +179,4 @@ widget_class_setup(lua_State *L)
179 179 NULL);
180 180 }
181 181
182   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  182 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  widget.h
@@ -75,4 +75,4 @@ void widget_class_setup(lua_State *);
75 75
76 76 #endif
77 77
78   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  78 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  widgets/box.c
@@ -150,4 +150,4 @@ BOX_WIDGET_CONSTRUCTOR(hbox)
150 150
151 151 #undef BOX_WIDGET_CONSTRUCTOR
152 152
153   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  153 +// vim: ft=c:et:sw=4:ts=8:sts=4:tw=80
2  widgets/common.c
@@ -189,4 +189,4 @@ luaH_widget_destroy(lua_State *L)
189 189 return 0;
190 190 }
191 191
192   -// vim: ft=c:et:sw=4:ts=8:sts=4:enc=utf-8:tw=80
  192