Wed, 01 Sep 2010 03:59:23 +0100
Initial set of changes to make 'insert' mode 'normal', eradicate 'command' mode and adjust key bindings appropriately (following common binding patterns from other browsers, and in places the nano text editor)
-------------------------- -- WebKit WebView class -- -------------------------- -- Webview class table webview = {} -- Table of functions which are called on new webview widgets. webview.init_funcs = { -- Set global properties set_global_props = function (view, w) -- Set proxy options local proxy = globals.http_proxy or os.getenv("http_proxy") if proxy then view:set_prop('proxy-uri', proxy) end view:set_prop('user-agent', globals.useragent) -- Set ssl options if globals.ssl_strict ~= nil then view:set_prop('ssl-strict', globals.ssl_strict) end if globals.ca_file and os.exists(globals.ca_file) then view:set_prop('ssl-ca-file', globals.ca_file) -- Warning: update the following variable if 'ssl-ca-file' is -- changed anywhere else. w.checking_ssl = true end end, -- Update window and tab titles title_update = function (view, w) view:add_signal("property::title", function (v) w:update_tab_labels() if w:is_current(v) then w:update_win_title() end end) end, -- Update uri label in statusbar uri_update = function (view, w) view:add_signal("property::uri", function (v) w:update_tab_labels() if w:is_current(v) then w:update_uri(v) end end) end, -- Update scroll widget scroll_update = function (view, w) view:add_signal("expose", function (v) if w:is_current(v) then w:update_scroll(v) end end) end, -- Update progress widget progress_update = function (view, w) for _, sig in ipairs({"load-status", "property::progress"}) do view:add_signal(sig, function (v) if w:is_current(v) then w:update_progress(v) w:update_ssl(v) end end) end end, -- Display hovered link in statusbar link_hover_display = function (view, w) view:add_signal("link-hover", function (v, link) if w:is_current(v) and link then w.sbar.l.uri.text = "Link: " .. lousy.util.escape(link) end end) view:add_signal("link-unhover", function (v) if w:is_current(v) then w:update_uri(v) end end) end, -- Clicking a form field automatically enters insert mode form_insert_mode = function (view, w) view:add_signal("form-active", function () (w.search_state or {}).marker = nil w:set_mode("normal") end) view:add_signal("root-active", function () (w.search_state or {}).marker = nil w:set_mode() end) end, -- Stop key events hitting the webview if the user isn't in insert mode mode_key_filter = function (view, w) view:add_signal("key-press", function () if not w:is_mode("normal") then return true end end) end, -- Try to match a button event to a users button binding else let the -- press hit the webview. button_bind_match = function (view, w) -- Match button press view:add_signal("button-release", function (v, mods, button) (w.search_state or {}).marker = nil if w:hit(mods, button) then return true end end) end, -- Reset the mode on navigation mode_reset_on_nav = function (view, w) view:add_signal("load-status", function (v, status) if w:is_current(v) and status == "provisional" then w:set_mode() end end) end, -- Domain properties domain_properties = function (view, w) view:add_signal("load-status", function (v, status) if status ~= "provisional" then return end local domain = (v.uri and string.match(v.uri, "^%a+://([^/]*)/?")) or "about:blank" if string.match(domain, "^www.") then domain = string.sub(domain, 5) end local props = lousy.util.table.join(domain_props.all or {}, domain_props[domain] or {}) for k, v in pairs(props) do info("Domain prop: %s = %s (%s)", k, tostring(v), domain) view:set_prop(k, v) end end) end, -- Action to take on mime type decision request. mime_decision = function (view, w) -- Return true to accept or false to reject from this signal. view:add_signal("mime-type-decision", function (v, link, mime) info("Requested link: %s (%s)", link, mime) -- i.e. block binary files like *.exe --if mime == "application/octet-stream" then -- return false --end end) end, -- Action to take on window open request. window_decision = function (view, w) -- 'link' contains the download link -- 'reason' contains the reason of the request (i.e. "link-clicked") -- return TRUE to handle the request by yourself or FALSE to proceed -- with default behaviour view:add_signal("new-window-decision", function (v, link, reason) info("New window decision: %s (%s)", link, reason) if reason == "link-clicked" then window.new({ link }) return true end w:new_tab(link) end) end, create_webview = function (view, w) -- Return a newly created webview in a new tab view:add_signal("create-web-view", function (v) return w:new_tab() end) end, -- Action to take on download request. download_request = function (view, w) -- 'link' contains the download link -- 'filename' contains the suggested filename (from server or webkit) view:add_signal("download-request", function (v, link, filename) if not filename then return end -- Make download dir os.execute(string.format("mkdir -p %q", globals.download_dir)) local dl = globals.download_dir .. "/" .. filename local wget = string.format("wget -q %q -O %q", link, dl) info("Launching: %s", wget) luakit.spawn(wget) end) end, -- Creates context menu popup from table (and nested tables). -- Use `true` for menu separators. populate_popup = function (view, w) view:add_signal("populate-popup", function (v) return { true, { "_Toggle Source", function () w:toggle_source() end }, { "_Zoom", { { "Zoom _In", function () w:zoom_in(globals.zoom_step) end }, { "Zoom _Out", function () w:zoom_out(globals.zoom_step) end }, true, { "Zoom _Reset", function () w:zoom_reset() end }, }, }, } end) end, -- Action to take on resource request. resource_request_decision = function (view, w) view:add_signal("resource-request-starting", function(v, uri) if luakit.verbose then print("Requesting: "..uri) end -- Return false to cancel the request. end) end, } -- These methods are present when you index a window instance and no window -- method is found in `window.methods`. The window then checks if there is an -- active webview and calls the following methods with the given view instance -- as the first argument. All methods must take `view` & `w` as the first two -- arguments. webview.methods = { reload = function (view, w) view:reload() end, -- Property functions get = function (view, w, k) return view:get_prop(k) end, set = function (view, w, k, v) view:set_prop(k, v) end, -- evaluate javascript code and return string result eval_js = function (view, w, script, file) return view:eval_js(script, file or "(inline)") end, -- evaluate javascript code from file and return string result eval_js_from_file = function (view, w, file) local fh, err = io.open(file) if not fh then return error(err) end local script = fh:read("*a") fh:close() return view:eval_js(script, file) end, -- close the current tab close_tab = function (view, w) w.tabs:remove(view) view.uri = "about:blank" view:destroy() w:update_tab_count() w:update_tab_labels() end, -- Toggle source view toggle_source = function (view, w, show) if show == nil then show = not view:get_view_source() end view:set_view_source(show) end, -- Zoom functions zoom_in = function (view, w, step) view:set_prop("zoom-level", view:get_prop("zoom-level") + step) end, zoom_out = function (view, w, step) local value = view:get_prop("zoom-level") - step view:set_prop("zoom-level", ((value > 0.01) and value) or 0.01) end, zoom_reset = function (view, w) view:set_prop("zoom-level", 1.0) end, -- Searching functions start_search = function (view, w, text) if string.match(text, "^[\?\/]") then w:set_mode("search") local i = w.ibar.input i.text = text i:focus() i:set_position(-1) else return error("invalid search term, must start with '?' or '/'") end end, search = function (view, w, text, forward) if forward == nil then forward = true end -- Get search state (or new state) if not w.search_state then w.search_state = {} end local s = w.search_state -- Get search term text = text or s.last_search if not text or #text == 0 then return w:clear_search() end s.last_search = text if s.forward == nil then -- Haven't searched before, save some state. s.forward = forward s.marker = view:get_scroll_vert() else -- Invert direction if originally searching in reverse forward = (s.forward == forward) end view:search(text, false, forward, true); end, clear_search = function (view, w) view:clear_search() w.search_state = {} end, -- Webview scroll functions scroll_vert = function (view, w, value) local cur, max = view:get_scroll_vert() if type(value) == "string" then value = lousy.util.parse_scroll(cur, max, value) end view:set_scroll_vert(value) end, scroll_horiz = function (view, w, value) local cur, max = view:get_scroll_horiz() if type(value) == "string" then value = lousy.util.parse_scroll(cur, max, value) end view:set_scroll_horiz(value) end, -- vertical scroll of a multiple of the view_size scroll_page = function (view, w, value) local cur, max, size = view:get_scroll_vert() view:set_scroll_vert(cur + (size * value)) end, -- History traversing functions back = function (view, w, n) view:go_back(n or 1) end, forward = function (view, w, n) view:go_forward(n or 1) end, } function webview.new(w, uri) local view = widget{type = "webview"} -- Call webview init functions for k, func in pairs(webview.init_funcs) do func(view, w) end if uri then view.uri = uri end view.show_scrollbars = false return view end -- Insert webview method lookup on window structure table.insert(window.indexes, 1, function (w, k) -- Get current webview local view = w.tabs:atindex(w.tabs:current()) if not view then return end -- Lookup webview method local func = webview.methods[k] if not func then return end -- Return webview method wrapper function return function (_, ...) return func(view, w, ...) end end) -- vim: et:sw=4:ts=8:sts=4:tw=80