Contents
- General Usage
- Navigation
- GUI Function
-
Look and Feel
- Mode line buttons for basic browser control
- Big Hint Numbers
- Hide Scroll Bars
- Default Zoom Level
- Darken the current page
- Make the current page readable by removing clutter with Arc90's Readability
- Use Readable (a Readability competitor) without changing buffer location
- View the current page through InstaPaper's "Text View"
- Ask before closing the window
- Render the web page with default (custom) colors
- Auto-hide the Minibuffer
- Auto-hide the Minibuffer and Mode Line
- Auto-hide the minibuffer and print text to the minibuffer, alternative implementation
- Disable Gif Animation
- Environment
- Downloads
- Specific Sites and Services
- Interaction with Other Programs
- Browsing through Tor
- Development Tools
- Automation
1. General Usage
1.1. Select Current Page with Browser Object Commands
Certain commands (like 'copy', 'save' etc.) prompt for a link and show hint numbers; when you just want to select the url of the current buffer, you can just type 0 get it. Let us say you want to save the current buffer, just type the following
M-x save RET 0 RET
and you will be prompted to choose a file path. With that, you could also duplicate buffers, which is a function found in many browsers:
M-x follow-new-buffer RET 0
Note: This will not duplicate the state (like colum/line position etc.) of the buffer.
If you want to bind this to a key, you can use something like the following:
interactive("duplicate-buffer", "Duplicate buffer", function (I) { browser_object_follow(I.buffer, OPEN_NEW_BUFFER, I.buffer.current_uri.spec); }); define_key(content_buffer_normal_keymap, "M-N", "duplicate-buffer");
1.2. Set Homepage to a File in the Home Directory
The following code is how to set your homepage to a file in your home directory, in a way that is cross-platform safe, without hard-coding the path.
On older xul/firefox version syntax is
let ( home = get_home_directory()) { home.append("foo.html"); homepage = home.path; }
If you get an error on latest firefox 45.01 the following works
let home = get_home_directory(); home.append("foo.html"); homepage = home.path;
1.3. Generate a PDF file
Useful for printing:
M-x print-buffer
1.4. Make each page's title available to copy to the clipboard
Note: gets the content of the title HTML tag enclosed within the page's head tag, not the value of the title attribute of other tags.
Add this browser object to your ConkerorRC:
define_browser_object_class( "page-title", "Get the title of the current page", function (I, prompt) { check_buffer(I.buffer, content_buffer); yield co_return(I.buffer.document.title); }); define_key(content_buffer_normal_keymap, "* p", "browser-object-page-title"); // So `* p c` will copy the title of the current buffer
2. Navigation
2.1. Follow Links in a New Buffer with a One-Key Binding
Since Oct 27, 2008, Conkeror now includes the commands follow-new-buffer, follow-new-buffer-background, and follow-new-window. All you have to do is bind the key of your choice, as in the following example.
define_key(content_buffer_normal_keymap, "d", "follow-new-buffer");
Notes: It is also possible to get the same behavior by prefixing the commands with C-u. For example C-u f will follow the link in new buffer.
2.2. Keyboard Shortcuts for Often-Used Sites
Here is an example of how to bind a key to go to a specific website. Because the command is defined as an alias of the follow command, the prefix key C-u will open the site in a new buffer.
interactive("open-gmail", "Go to gmail", "follow", $browser_object = "http://gmail.com/"); define_key(content_buffer_normal_keymap, "f1", "open-gmail");
2.3. Open Multiple "Bookmarks" with One Key
A quick way to open multiple web pages you visit frequently, at the same time.
interactive("open-all", "opens bookmarks I visit frequently", function(I){ load_url_in_new_buffer("http://www.UrlNr1.com",I.window); load_url_in_new_buffer("http://www.UrlNr2.org",I.window); load_url_in_new_buffer("http://www.UrlNr3.org",I.window); }); define_key(content_buffer_normal_keymap, "f1", "open-all");
2.4. Navigate with Mouse Buttons
Add the following to your RC to go back forward with middle/right mouse buttons. (FIXME: Middle button doesn't work on scrollable pages).
mouse_back = 1; mouse_forward = 2; { let navigate_click = function(event) { let w = get_recent_conkeror_window().buffers.current.web_navigation; if (event.button == mouse_back && w.canGoBack) w.goBack(); else if (event.button == mouse_forward && w.canGoForward) w.goForward(); else return; event.stopPropagation(); } let install_handler = function (buffer) { buffer.browser.addEventListener("click", navigate_click, true); } add_hook("create_buffer_hook", install_handler); }
2.5. Open Middle-Clicked Links in New Buffers
require("clicks-in-new-buffer.js");
You can control whether buffers are created in the foreground or background (foreground is default).
// Set to either OPEN_NEW_BUFFER or OPEN_NEW_BUFFER_BACKGROUND clicks_in_new_buffer_target = OPEN_NEW_BUFFER_BACKGROUND; // Now buffers open in background.
You can control the mouse button which triggers buffer creation (middle is default).
// Set to 0 = left mouse, 1 = middle mouse, 2 = right mouse clicks_in_new_buffer_button = 2; // Now right mouse follows links in new buffers.
2.6. Selection Searches
For example, pressing w will prompt you what to search on Wikipedia (with autocomplete), or pressing W will automatically search what you have currently selected. Since it does more than selection searches, it could use a better name. Maybe webjump-shortcuts.
// selection searches function create_selection_search(webjump, key) { interactive(webjump+"-selection-search", "Search " + webjump + " with selection contents", "find-url-new-buffer", $browser_object = function (I) { return webjump + " " + I.buffer.top_frame.getSelection();}); define_key(content_buffer_normal_keymap, key.toUpperCase(), webjump + "-selection-search"); interactive("prompted-"+webjump+"-search", null, function (I) { var term = yield I.minibuffer.read_url($prompt = "Search "+webjump+":", $initial_value = webjump+" ", $select = false); browser_object_follow(I.buffer, FOLLOW_DEFAULT, term); }); define_key(content_buffer_normal_keymap, key, "prompted-" + webjump + "-search"); } // examples // create_selection_search("g","l"); // create_selection_search("wikipedia","w"); // create_selection_search("dictionary","d"); // create_selection_search("myspace","y"); // create_selection_search("amazon","a"); // create_selection_search("youtube","u");
2.7. Client Redirect
See Client Redirect.
2.8. Browse buffer session history
With the following command you can go back or forward to any URL in the session history of the current buffer by selecting from a list of visited URLs (and without altering the history in any way).
interactive("browse-buffer-history", "Browse the session history for the current buffer", function browse_buffer_history (I) { var b = check_buffer(I.buffer, content_buffer); var history = b.web_navigation.sessionHistory; if (history.count > 1) { var entries = []; for(var i = 0 ; i < history.count ; i += 1) { entries[i] = history.getEntryAtIndex(i, false).URI.spec; } var url = yield I.minibuffer.read( $prompt = "Go back or forward to:", $completer = new all_word_completer($completions = entries), $default_completion = history.index > 0 ? entries[history.index - 1] : entries[history.index + 1], $auto_complete = "url", $auto_complete_initial = true, $auto_complete_delay = 0, $require_match = true); b.web_navigation.gotoIndex(entries.indexOf(url)); } else { I.window.minibuffer.message("No history"); } });
3. GUI Function
3.1. Bind Number Keys to Switch to Buffers 1-10
Handy as the number keys are unbound by default. I.e., 1 will switch to the first buffer, 2 to the second buffer, 0 to the tenth buffer. These bindings work as expected with the tab-bar module.
function define_switch_buffer_key (key, buf_num) { define_key(default_global_keymap, key, function (I) { switch_to_buffer(I.window, I.window.buffers.get_buffer(buf_num)); }); } for (let i = 0; i < 10; ++i) { define_switch_buffer_key(String((i+1)%10), i); }
3.2. Switch to previously open buffer
This will switch to the buffer shown before the current buffer, if such a buffer exists.
interactive("switch-to-other-buffer", "Switch to the previously open buffer", function (I) { var blist = I.window.buffers.buffer_list if (blist.length > 1) switch_to_buffer(I.window, blist[1]); });
(This is similar in effect to the Emacs Lisp code (switch-to-buffer (other-buffer)).)
3.3. Order buffers by last access time
This is a version of switch-to-buffer that displays the list of completions in last-visited (temporal) order rather than spatial. Without entering any text, it will act to switch to the last buffer displayed (though that behavior is identical to the stock switch-to-buffer).
interactive("switch-to-recent-buffer", "Prompt for a buffer and switch to it, displaying the list in last-visited order.", function (I) { switch_to_buffer( I.window, (yield I.minibuffer.read_buffer( $prompt = "Switch to buffer:", $buffers = I.window.buffers.buffer_history, $default = (I.window.buffers.count > 1 ? I.window.buffers.buffer_history[1] : I.buffer)))); }); define_key(default_global_keymap, "C-x b", "switch-to-recent-buffer");
No modification of read_buffer is required. The command switch-to-buffer, as well as buffer-previous and buffer-next (usually bound to M-p and M-n), retain their original semantics in terms of the spatial ordering; only the new command switch-to-recent-buffer will display the temporal order.
3.4. Url Remoting Multiple Targets
Conkeror provides a command line switch +u which works similarly to the C-u key for interactive commands, except that it works for commands given on the command line with -f. The function of +u can be hijacked, so to speak, to work with url_remoting_fn, to allow selection of the target in which to open a remoted url. Here is an example that can easily be modified to your preferred choice of alternative targets.
/* For url_remoting_fn; load in a new buffer. If +u is given on the * command line, do so in the background. */ function load_url_in_new_buffer_perhaps_bg(url, ctx) { create_buffer_in_current_window( buffer_creator(content_buffer, $opener = ctx, $load = url), ctx.prefix_argument ? OPEN_NEW_BUFFER_BACKGROUND : OPEN_NEW_BUFFER, !ctx.prefix_argument); } url_remoting_fn = load_url_in_new_buffer_perhaps_bg;
3.5. Restore Killed Buffer Url
The following code remembers all urls of buffers that you close during a session and provides the command `restore-killed-buffer-url' to reopen them.
// I think by the time kill_buffer_hook runs the buffer is gone so I // patch kill_buffer var kill_buffer_original = kill_buffer_original || kill_buffer; var killed_buffer_urls = []; kill_buffer = function (buffer, force) { if (buffer.display_uri_string) { killed_buffer_urls.push(buffer.display_uri_string); } kill_buffer_original(buffer,force); }; interactive("restore-killed-buffer-url", "Loads url from a previously killed buffer", function restore_killed_buffer_url (I) { if (killed_buffer_urls.length !== 0) { var url = yield I.minibuffer.read( $prompt = "Restore killed url:", $completer = new all_word_completer($completions = killed_buffer_urls), $default_completion = killed_buffer_urls[killed_buffer_urls.length - 1], $auto_complete = "url", $auto_complete_initial = true, $auto_complete_delay = 0, $require_match = true); load_url_in_new_buffer(url); } else { I.window.minibuffer.message("No killed buffer urls"); } });
3.6. Revive Buffer (Undo closed tab)
The following code is similar to the above, except buffer history is also restored. The command it defines is called revive-buffer.
define_key(default_global_keymap, "C-T", "revive-buffer"); var kill_buffer_original = kill_buffer_original || kill_buffer; var killed_buffer_urls = []; var killed_buffer_histories = []; // remember_killed_buffer kill_buffer = function (buffer, force) { var hist = buffer.web_navigation.sessionHistory; if (buffer.display_uri_string && hist) { killed_buffer_histories.push(hist); killed_buffer_urls.push(buffer.display_uri_string); } kill_buffer_original(buffer,force); }; interactive("revive-buffer", "Loads url from a previously killed buffer", function restore_killed_buffer (I) { if (killed_buffer_urls.length !== 0) { var url = yield I.minibuffer.read( $prompt = "Restore killed url:", $completer = new all_word_completer($completions = killed_buffer_urls), $default_completion = killed_buffer_urls[killed_buffer_urls.length - 1], $auto_complete = "url", $auto_complete_initial = true, $auto_complete_delay = 0, $require_match = true); var window = I.window; var creator = buffer_creator(content_buffer); var idx = killed_buffer_urls.indexOf(url); // Create the buffer var buf = creator(window, null); // Recover the history buf.web_navigation.sessionHistory = killed_buffer_histories[idx]; // This line may seem redundant, but it's necessary. var original_index = buf.web_navigation.sessionHistory.index; buf.web_navigation.gotoIndex(original_index); // Focus the new tab window.buffers.current = buf; // Remove revived from cemitery killed_buffer_urls.splice(idx,1); killed_buffer_histories.splice(idx,1); } else { I.window.minibuffer.message("No killed buffer urls"); } });
It has a slight bug. If multiple killed buffers had the same url but different histories, you'll always revive the first one, no matter which one you choose at the prompt. This can be fixed if minibuffer.read can be made to return the entry-index, instead of the entry itself.
3.6.1. Improved version
By scottj. Note that the same buffer can be restored multiple times, and that there is only one history object shared by all such copies. This may be a feature or a bug. -- PhilHudson 2015-01-27 22:13:37
4. Look and Feel
4.1. Mode line buttons for basic browser control
Simple GUI buttons can be enabled to control conkeror. They are intended to be unobtrusive and to steal as little screen space as possible. Clicking on them executes a conkeror command. Hovering over them tells you the command and the corresponding keystroke.
The buttons are intended to make conkeror usable for a casual user and also to aid the novice user while they become familiar with conkeror's interface.
load_paths.unshift("chrome://conkeror-contrib/content/"); require("mode-line-buttons.js"); mode_line_add_buttons(standard_mode_line_buttons, true);
4.2. Big Hint Numbers
register_user_stylesheet( "data:text/css," + escape( "@namespace url(\"http://www.w3.org/1999/xhtml\");\n" + "span.__conkeror_hint {\n"+ " font-size: 18px !important;\n"+ " line-height: 18px !important;\n"+ "}"));
4.3. Hide Scroll Bars
Moved to HideScrollbars.
4.4. Default Zoom Level
function my_zoom_set (buffer) { browser_zoom_set(buffer, false, 150); } add_hook('create_buffer_late_hook', my_zoom_set);
If the default zoom levels are not enough for you redefine the zoom_levels variable:
zoom_levels = [ 1, 10, 25, 50, 75, 90, 100, 110, 120, 125, 130, 140, 150, 200, 300, 500, 1000, 2000 ];
4.5. Darken the current page
function darken_page (I) { var styles='* { background: black !important; color: grey !important; }'+ ':link, :link * { color: #4986dd !important; }'+ ':visited, :visited * { color: #d75047 !important; }'; var document = I.buffer.document; var newSS=document.createElement('link'); newSS.rel='stylesheet'; newSS.href='data:text/css,'+escape(styles); document.getElementsByTagName("head")[0].appendChild(newSS); } interactive("darken-page", "Darken the page in an attempt to save your eyes.", darken_page);
This is a common enough requirement for me that I bind it to C-d:
define_key(content_buffer_normal_keymap, "C-d", "darken-page");
4.6. Make the current page readable by removing clutter with Arc90's Readability
From http://lab.arc90.com/experiments/readability/ "Readability™ is a simple tool that makes reading on the Web more enjoyable by removing the clutter around what you're reading."
interactive("readability_arc90", "Readability is a simple tool that makes reading on the web more enjoyable by removing the clutter around what you are reading", function readability_arc90(I) { var document = I.window.buffers.current.document; var readConvertLinksToFootnotes = false; var readStyle = 'style-newspaper'; var readSize = 'size-medium'; var readMargin = 'margin-wide'; var _readability_readStyle = document.createElement('SCRIPT'); _readability_readStyle.text = 'var readStyle = \'' + readStyle + '\';'; document.getElementsByTagName('head')[0].appendChild(_readability_readStyle); var _readability_readSize = document.createElement('SCRIPT'); _readability_readSize.text = 'var readSize = \'' + readSize + '\';'; document.getElementsByTagName('head')[0].appendChild(_readability_readSize); var _readability_readMargin = document.createElement('SCRIPT'); _readability_readMargin.text = 'var readMargin = \'' + readMargin + '\';'; document.getElementsByTagName('head')[0].appendChild(_readability_readMargin); var _readability_readConvertLinksToFootnotes = document.createElement('SCRIPT'); _readability_readConvertLinksToFootnotes.text = 'var readConvertLinksToFootnotes = ' + readConvertLinksToFootnotes + ';'; document.getElementsByTagName('head')[0].appendChild(_readability_readConvertLinksToFootnotes); var _readability_script = document.createElement('script') _readability_script.type='text/javascript' _readability_script.src='http://lab.arc90.com/experiments/readability/js/readability.js?x='+(Math.random()) document.documentElement.appendChild(_readability_script) var _readability_css = document.createElement('link') _readability_css.rel = 'stylesheet' _readability_css.href = 'http://lab.arc90.com/experiments/readability/css/readability.css' _readability_css.type = 'text/css' _readability_css.media = 'all' document.documentElement.appendChild(_readability_css) var _readability_print_css = document.createElement('link') _readability_print_css.rel = 'stylesheet' _readability_print_css.href = 'http://lab.arc90.com/experiments/readability/css/readability-print.css' _readability_print_css.media = 'print' _readability_print_css.type = 'text/css' document.getElementsByTagName('head')[0].appendChild(_readability_print_css) });
Bind it to 'z'
define_key(content_buffer_normal_keymap, "z", "readability_arc90");
You can also make this a webjump. (This would produce a more up to date version with out any editing required.) Simply copy the code from the readability button and replace <bookmarklet code here> with it.
define_webjump("readability", "<bookmarklet code here>");
4.7. Use Readable (a Readability competitor) without changing buffer location
Readable has several advantages over Readability, including being faster, having less clutter, and not changing scrolling behavior. As a webjump though it will mangle the buffer location into javascript:foo and then the page won't display when the session is reloaded. The code below fixes that by making it a command instead of a webjump.
var readable_options = { text_font: 'quote(Palatino%20Linotype),'+ '%20Palatino,%20quote(Book%20Antigua),'+ '%20Georgia,%20serif', text_font_monospace: 'quote(Courier%20New),'+ '%20Courier,%20monospace', text_font_header: 'quote(Times%20New%20Roman),'+ '%20Times,%20serif', text_size: '18px', text_line_height: '1.5', box_width: '30em', color_text: '#282828', color_background: '#F5F5F5', color_links: '#0000FF', text_align: 'normal', base: 'blueprint', custom_css: '' }; interactive("readable", "", function (I) { var document = I.buffer.document; var window = document.defaultView.wrappedJSObject; if (document.getElementsByTagName('body').length == 0) return; if (window.$readable) { if (window.$readable.bookmarkletTimer) return; } else window.$readable = {}; window.$readable.bookmarkletTimer = true; window.$readable.options = readable_options; if (window.$readable.bookmarkletClicked) { window.$readable.bookmarkletClicked(); return; } var el = document.createElement('script'); el.setAttribute('src', 'http://readable-static.tastefulwords.com/target.js'+ '?rand='+encodeURIComponent(Math.random())); document.getElementsByTagName('body')[0].appendChild(el); });
4.8. View the current page through InstaPaper's "Text View"
Use this to view web pages thorugh InstaPaper's text view (similar to the Readability approach above). For more information, visit http://www.instapaper.com/extras and scroll down to "Instapaper Text bookmarklet"
interactive("render_instapaper", "Render page with InstaPaper's Text view.", function (I) { var d = I.window.buffers.current.document; if(!d.body) throw('Please wait until the page has loaded.'); browser_object_follow( I.window.buffers.current, OPEN_CURRENT_BUFFER, 'http://www.instapaper.com/text?u='+encodeURIComponent(d.location.href)); I.window.minibuffer.message("Rendering with InstaPaper ..."); });
Bind it to 'a':
define_key(content_buffer_normal_keymap, "a", "render_instapaper");
4.9. Ask before closing the window
add_hook("window_before_close_hook", function () { var w = get_recent_conkeror_window(); var result = (w == null) || "y" == (yield w.minibuffer.read_single_character_option( $prompt = "Quit Conkeror? (y/n)", $options = ["y", "n"])); yield co_return(result); });
And never again should you close conkeror by accident.
4.10. Render the web page with default (custom) colors
This is how you can force xulrunner to render the web page with default black on white coloring.
First thing is to disable use of system colors in your conkeror. You can do that in 3 different ways:
- in profile in ~/.conkeror.mozdev.org/conkeror/$PROFILE_ID/prefs.js by setting these options. You better quit conkeror before making these changes to the file.
- in about:config which will be saved in prefs.js
- in .conkerorrc (prefered way).
user_pref("browser.display.use_system_colors", false); user_pref("browser.active_color", "#EE0000"); user_pref("browser.anchor_color", "#0000EE"); user_pref("browser.display.background_color", "#FFFFFF"); user_pref("browser.display.foreground_color", "#000000"); user_pref("browser.visited_color", "#551A8B");
And you can have make it possible to toggle between default and document colors:
interactive("colors-toggle", "toggle between document and forced colors", function (I) { var p = "browser.display.use_document_colors"; if (get_pref(p)) session_pref(p, false); else session_pref(p, true); }); define_key(content_buffer_normal_keymap, "f6", "colors-toggle");
Next issue is with input elements. To override it's colors you have to use this CSS hack.
Create a file ~/.conkeror.css with this content
@-moz-document url-prefix(http) { input { border: 1px inset gray; background-color: white; color: black; -moz-appearance: none !important; } textarea { border: 1px inset gray; background-color: white; color: black; -moz-appearance: none !important; } select { border: 1px inset gray; background-color: white; color: black; -moz-appearance: none !important; } input[type="radio"], input[type="checkbox"] { border: 1px inset gray ! important; background-color: white ! important; color: ThreeDFace ! important; -moz-appearance: none !important; } *|*::-moz-radio { background-color: white; -moz-appearance: none !important; } button, input[type="reset"], input[type="button"], input[type="submit"] { border: 1px outset gray; background-color: #eeeeee; color: black; -moz-appearance: none !important; } button:hover, input[type="reset"]:hover, input[type="button"]:hover, input[type="submit"]:hover { background-color: lightgray !important; -moz-appearance: none !important; } body { background-color: white; color: black; -moz-appearance: none !important; } }
To put this file in use, load this file in the .conkerorrc like this
register_user_stylesheet('file:///home/$USER/.conkeror.css');
To toggle the style sheet's' active state:
var global_css_registered=true; register_user_stylesheet('file:///home/$USER/.conkeror.css'); function toggle_global_css(I){ global_css_registered=global_css_registered ? false : true; if(global_css_registered){ register_user_stylesheet('file:///home/$USER/.conkeror.css'); }else{ unregister_user_stylesheet('file:///home/$USER/.conkeror.css'); } } interactive("toggle-global-css", "Toggle global.css", toggle_global_css); define_key(default_global_keymap, "C-t", "toggle-global-css");
4.11. Auto-hide the Minibuffer
/// /// Auto-hide Minibuffer /// var minibuffer_autohide_timer = null; var minibuffer_autohide_message_timeout = 3000; //milliseconds to show messages var minibuffer_mutually_exclusive_with_mode_line = true; function hide_minibuffer (window) { window.minibuffer.element.collapsed = true; if (minibuffer_mutually_exclusive_with_mode_line && window.mode_line) window.mode_line.container.collapsed = false; } function show_minibuffer (window) { window.minibuffer.element.collapsed = false; if (minibuffer_mutually_exclusive_with_mode_line && window.mode_line) window.mode_line.container.collapsed = true; } add_hook("window_initialize_hook", hide_minibuffer); // for_each_window(hide_minibuffer); // initialize existing windows var old_minibuffer_restore_state = (old_minibuffer_restore_state || minibuffer.prototype._restore_state); minibuffer.prototype._restore_state = function () { if (minibuffer_autohide_timer) { timer_cancel(minibuffer_autohide_timer); minibuffer_autohide_timer = null; } if (this.current_state) show_minibuffer(this.window); else hide_minibuffer(this.window); old_minibuffer_restore_state.call(this); }; var old_minibuffer_show = (old_minibuffer_show || minibuffer.prototype.show); minibuffer.prototype.show = function (str, force) { var w = this.window; show_minibuffer(w); old_minibuffer_show.call(this, str, force); if (minibuffer_autohide_timer) timer_cancel(minibuffer_autohide_timer); minibuffer_autohide_timer = call_after_timeout( function () { hide_minibuffer(w); }, minibuffer_autohide_message_timeout); }; var old_minibuffer_clear = (old_minibuffer_clear || minibuffer.prototype.clear); minibuffer.prototype.clear = function () { if (minibuffer_autohide_timer) { timer_cancel(minibuffer_autohide_timer); minibuffer_autohide_timer = null; } if (! this.current_state) hide_minibuffer(this.window); old_minibuffer_clear.call(this); };
4.12. Auto-hide the Minibuffer and Mode Line
This hack, replacing part of the above, auto hides the mode line together with the minibuffer. This is useful in full screen on a netbook; all you see is web.
var minibuffer_autohide_with_mode_line = true; function hide_minibuffer (window) { window.minibuffer.element.collapsed = true; if (minibuffer_autohide_with_mode_line && window.mode_line) window.mode_line.container.collapsed = true; } function show_minibuffer (window) { window.minibuffer.element.collapsed = false; if (minibuffer_autohide_with_mode_line && window.mode_line) window.mode_line.container.collapsed = false; }
4.13. Auto-hide the minibuffer and print text to the minibuffer, alternative implementation
This implementation features a more object-oriented auto-hiding as well as an arguably simpler way to print text to the minibuffer.
var minibuffer_autohide_message_timeout = 3000; var minibuffer_autohide_timer = null; var minibuffer_mutually_exclusive_with_mode_line = true; var old_minibuffer_restore_state = (old_minibuffer_restore_state || minibuffer.prototype._restore_state); var old_minibuffer_show = (old_minibuffer_show || minibuffer.prototype.show); var old_minibuffer_clear = (old_minibuffer_clear || minibuffer.prototype.clear); show_minibuffer = function (window) { window.minibuffer.element.collapsed = false; if (minibuffer_mutually_exclusive_with_mode_line && window.mode_line) window.mode_line.container.collapsed = true; }; hide_minibuffer = function (window) { window.minibuffer.element.collapsed = true; if (minibuffer_mutually_exclusive_with_mode_line && window.mode_line) window.mode_line.container.collapsed = false; }; minibuffer.prototype._restore_state = function () { if (minibuffer_autohide_timer) { timer_cancel(minibuffer_autohide_timer); minibuffer_autohide_timer = null; } if (this.current_state) this.show(); else hide_minibuffer(this.window); old_minibuffer_restore_state.call(this); }; minibuffer.prototype.hide = function () { hide_minibuffer(this.window); }; minibuffer.prototype.show = function (str, force, hide_after_timeout) { var w = this.window; var self = this; show_minibuffer(this.window); old_minibuffer_show.call(this, str, force); if (minibuffer_autohide_timer) timer_cancel(minibuffer_autohide_timer); if (hide_after_timeout || hide_after_timeout == null) { minibuffer_autohide_timer = call_after_timeout( function (I) {self.hide();}, minibuffer_autohide_message_timeout); } }; minibuffer.prototype.clear = function () { if (minibuffer_autohide_timer) { timer_cancel(minibuffer_autohide_timer); minibuffer_autohide_timer = null; } if (!this.current_state) this.hide(); old_minibuffer_clear.call(this); }; add_hook("window_initialize_hook", function (I) {I.window.minibuffer.hide();});
4.13.1. Example usage
This will show the page URL in the minibuffer for minibuffer_autohide_message_timeout milliseconds and then hide itself:
interactive('show-page-url', 'Show the URL of the page', function(I) {I.window.minibuffer.show(I.buffer.document.location.href);});
This will show the page title in the minibuffer forever until another event closes it:
interactive('show-page-title-forever', 'Show the page title of the page', function(I) {I.window.minibuffer.show(I.buffer.document.title, false, true);});
4.14. Disable Gif Animation
session_pref("image.animation_mode", "none");
5. Environment
5.1. Cache Settings
Clearing caches:
M-: cache_clear(CACHE_ALL) M-: cache_clear(CACHE_DISK) M-: cache_clear(CACHE_MEMORY) M-: cache_clear(CACHE_OFFLINE)
Disabling caches:
M-: cache_disable(CACHE_ALL) M-: cache_disable(CACHE_DISK) M-: cache_disable(CACHE_MEMORY) M-: cache_disable(CACHE_OFFLINE)
Enabling caches:
M-: cache_enable(CACHE_ALL) M-: cache_enable(CACHE_DISK) M-: cache_enable(CACHE_MEMORY) M-: cache_enable(CACHE_OFFLINE)
5.2. Proxy Settings
Set all protocols to use the same proxy server and port (or none) for the current session only.
//set the proxy server for this session only proxy_server_default = "proxy.server.com"; proxy_port_default = 80; function set_proxy_session (window, server, port) { if (server == "N") { session_pref('network.proxy.type', 0); //direct connection window.minibuffer.message("Direction connection to the internet enabled for this session"); } else { if (server == "") server = proxy_server_default; if (port == "") port = proxy_port_default; session_pref('network.proxy.ftp', server); session_pref('network.proxy.gopher', server); session_pref('network.proxy.http', server); session_pref('network.proxy.socks', server); session_pref('network.proxy.ssl', server); session_pref('network.proxy.ftp_port', port); session_pref('network.proxy.gopher_port', port); session_pref('network.proxy.http_port', port); session_pref('network.proxy.socks_port', port); session_pref('network.proxy.ssl_port', port); session_pref('network.proxy.share_proxy_settings', true); session_pref('network.proxy.type', 1); window.minibuffer.message("All protocols using "+server+":"+port+" for this session"); } } interactive("set-proxy-session", "set the proxy server for all protocols for this session only", function (I) { set_proxy_session( I.window, (yield I.minibuffer.read($prompt = "server ["+proxy_server_default+"] or N: ")), (yield I.minibuffer.read($prompt = "port ["+proxy_port_default+"]: "))); });
5.3. Default Charset
The following snippet sets the default charset, based on the environment variable LANG. Note that it is necessary to use user_pref instead of session_pref for this setting.
user_pref("intl.charset.default", getenv("LANG").split(".")[1]);
5.4. Binding Unsupported Keys
Some keyboards have additional keys or buttons beyond the common ones, that Conkeror cannot see. These may include so-called multimedia keys. Since January, 2011, most browser keys (like Back, Forward, Home, etc) are supported. For keys which produce no response whatsoever in Conkeror (not even a message in the minibuffer), one way to use them in Conkeror is to remap them at the OS level with a utility such as xmodmap. Typically, you map them to be function keys F13 and up. Here are instructions for doing so on a typical GNU/Linux desktop system.
- Discover which key code your multimedia key sends to X.
Open a terminal and run xev.
- Press the key or button you want to map.
In the output xev, look for keycode <number>, and take note of the number.
- Map the keycode to F13.
Open ~/.Xmodmap in an editor and add the following line (replace 234 with the keycode reported by xev):
keycode 234 = F13
Save the file, close the editor, and run the following command: xmodmap ~/.Xmodmap
- In conkeror, you can now bind commands to F13:
define_key(content_buffer_normal_keymap, 'f13', 'go-back');
For more information on this subject, see this howto.
6. Downloads
6.1. Default paths and filename transformations for downloads
function suggest_save_path_from_file_name (file_name, buffer) { var cwd = with_current_buffer(buffer, function (I) I.local.cwd); var file = cwd.clone(); for (let re in replace_map) { if (file_name.match(re)) { if (replace_map[re].path) { file = make_file(replace_map[re].path); } file_name = replace_map[re].transformer(file_name); } } file.append(file_name); return file.path; }
A snippet of my (aggressive) replace_map looks like this:
var replace_map = { ".": { "transformer": function (filename) { return filename.replace( /[\W ]+/g , "-" ) .replace( /^-+/ , "" ) .replace( /-+$/ , "" ) .replace( /-([^-]+)$/ , ".$1" ) .toLowerCase(); } }, "\.torrent$": { "path": "/media-files/", "transformer": function (filename) { return filename.replace( /isohunt-/i, "" ); } } };
Default actions will probably follow.
6.2. Remember the last save directory for downloads
Add the following code to your rc:
function update_save_path (info) { cwd = info.target_file.parent; } add_hook("download_added_hook", update_save_path);
Or, here is an alternative method. If, for some reason, you don't want to use the general purpose variable cwd to keep track of your download directory, this version uses its own private variable.
{ let _save_path = get_home_directory(); function update_save_path (info) { _save_path = info.target_file.parent.path; } add_hook("download_added_hook", update_save_path); suggest_save_path_from_file_name = function (filename, buffer) { let file = make_file(_save_path); file.append(filename); return file.path; } }
7. Specific Sites and Services
7.1. Subscribe to Atom/RSS feeds (google reader)
C-u subscribes to first encountered feed. C-u C-u pops-up a box with all available feeds on the page. It is oriented towards google-reader but could potentially be adapted to other sites by changing the 'reader' variable.
function subscribe_feed(I){ var f=false; var reader = 'http://google.com/reader/view/feed/'; var document= I.buffer.document; var ls=document.getElementsByTagName("link"); for(var i=0,l;l=ls[i];i++){ var t=l.getAttribute('type'); var r=l.getAttribute('rel'); if(t&&(t=='application/rss+xml'||t=='application/atom+xml')&&r&&r=='alternate'){ var h= l.getAttribute('href'); if(h.indexOf('http')!=0){ var p=(h.indexOf('/')!=0)?'/':document.location.pathname; h='http://'+document.location.hostname+p+h; } document.location=reader+h; f=true; }} if(!f) I.minibuffer.message('Oops. Can\'t find a feed.'); }; function subscribe_feed_all(I){ var document=I.buffer.document; var reader = 'http://google.com/reader/view/feed/'; var el=document.createElement('div'); el.style.zIndex=10000; el.style.position='absolute'; el.style.padding='2em'; el.style.top=0; el.style.backgroundColor='#ffffcc'; el.style.border='1px solid #008000'; el.style.color='#000 !important'; el.style.fontFamily='Arial, sans-serif'; el.style.textAlign='left'; el.innerHTML='View the following feeds in Google Reader:'; var found = false; var links = document.getElementsByTagName('link'); for (var i = 0, link;link = links[i];i++) { var type = link.getAttribute('type'); var rel = link.getAttribute('rel'); var title = link.getAttribute('title'); if (type && (type == 'application/rss+xml' || type == 'application/atom+xml') && rel && rel == 'alternate'){ var href = link.getAttribute('href'); if (!href.match(/^http/)){ var path = (href.match(/^\//)) ? '/' : document.location.pathname; href='http://' + document.location.hostname + path + href; } var previewLink = document.createElement('a'); previewLink.href = reader + href; previewLink.innerHTML = ((title) ? title : '') + ' - ' + href; previewLink.style.display='block'; previewLink.style.color='#00c'; previewLink.style.textDecoration='underline'; el.appendChild(previewLink); found = true; }} var close=document.createElement('a'); close.innerHTML='hhh Hide this box hhh'; close.href='#'; close.style.display='block'; close.style.marginTop='2em'; close.style.color='#00c'; close.style.textDecoration='underline'; close.addEventListener('click',function() { el.style.display='none'; return false; }, true); el.appendChild(close); function AddFeedBox() { document.body.insertBefore(el, document.body.firstChild); el.scrollIntoView(); } if (!found) I.minibuffer.message('Oops. Can\'t find any feeds for this page.'); else void(AddFeedBox()); }; interactive("subscribe-feed", "C-u Subscribes to first encountered feed." + "C-u C-u Pops-up a box with all available feeds on the page." + "It is oriented towards google-reader but could potentially be adapted to other sites by changing the 'reader' var.", alternates(subscribe_feed, subscribe_feed_all) ); define_key(default_global_keymap, "C-c s", "subscribe-feed");
7.2. Dictionary Search
Implements lookup for definitions from dict.org in all supported dictionaries. C-u triggers lookup for words with Levenshtein distance 1. C-u C-u triggers lookup for words which contain the given word as a substring. Many dictionaries and other lookup strategies are available (see source of the page).
FIXME: Unfocus at the end of the dict_ functions does not work. Is there a way to implement this without recurring to page-mode for dict.org?
function dict_definition(I){ check_buffer(I.buffer, content_buffer); let dict_url = 'http://www.dict.org/bin/Dict?Form=Dict2&Database=*&Query='+ encodeURIComponent( yield I.minibuffer.read( $prompt = "Dict: ", $initial_value = I.buffer.top_frame.getSelection())); browser_object_follow(I.buffer, OPEN_NEW_BUFFER, dict_url); unfocus(I.window, I.buffer); } function dict_substring(I){ check_buffer(I.buffer, content_buffer); let dict_url = 'http://www.dict.org/bin/Dict?Form=Dict2&Strategy=substring&Database=*&Query='+ encodeURIComponent( yield I.minibuffer.read( $prompt = "Dict (substring): ", $initial_value = I.buffer.top_frame.getSelection())); browser_object_follow(I.buffer, OPEN_NEW_BUFFER, dict_url); unfocus(I.window, I.buffer); } function dict_Levenshtein(I){ check_buffer(I.buffer, content_buffer); let dict_url = 'http://www.dict.org/bin/Dict?Form=Dict2&Strategy=lev&Database=*&Query='+ encodeURIComponent( yield I.minibuffer.read( $prompt = "Dict (Levenshtein): ", $initial_value = I.buffer.top_frame.getSelection())); browser_object_follow(I.buffer, OPEN_NEW_BUFFER, dict_url); unfocus(I.window, I.buffer); } interactive("dict", "Definitions of the word from dict.org in all supported dictionaries." + "C-u triggers lookup for words with Levenshtein distance 1." + "C-u C-u triggers lookup for words which contain the given word as a substring.", alternates(dict_definition, dict_Levenshtein, dict_substring) ); define_key(default_global_keymap, "f5", "dict");
7.3. Facebook Share
function facebook_share(I){ var d=I.buffer.document; var f='http://www.facebook.com/sharer'; var l=d.location, e=encodeURIComponent; var p='.php?src=bm&v=4&i=1279479932&u='+e(l.href)+'&t='+e(d.title); browser_object_follow(I.buffer, OPEN_NEW_BUFFER, f+p); }; interactive("facebook-share", "Share the current site on Facebook.", facebook_share);
7.4. Post to Instapaper
A full description of how to use these functions is available on the author's site.
interactive("instapaper", "Send the current page to InstaPaper.", function (I) { check_buffer(I.buffer, content_buffer); let posturl = 'https://www.instapaper.com/api/add?' + 'username=USERNAME&' + 'password=PASSWORD&url=' + encodeURIComponent(I.window.content.location.href) '&selection=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "Description (optional): ")); try { var content = yield send_http_request(load_spec({uri: posturl})); if (content.responseText == "201") { I.window.minibuffer.message("InstaPaper ok!"); } else { I.window.minibuffer.message("Error."); } } catch (e) { I.window.minibuffer.message("Error."); } }); interactive("instapaper-link", "Send the current link to InstaPaper.", function (I) { bo = yield read_browser_object(I) ; mylink = load_spec_uri_string(load_spec(encodeURIComponent(bo))); check_buffer(I.buffer, content_buffer); let posturl = 'https://www.instapaper.com/api/add?' + 'username=USERNAME&' + 'password=PASSWORD&url=' + mylink + '&title=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "Title (optional): ", $initial_value = bo.textContent)) + '&selection=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "Description (optional): ", $initial_value = "From: "+ I.buffer.title +" ("+I.window.content.location.href+")" )); try { var content = yield send_http_request(load_spec({uri: posturl})); if (content.responseText == "201") { I.window.minibuffer.message("InstaPaper ok!"); } else { I.window.minibuffer.message("Error."); } } catch (e) { I.window.minibuffer.message("Error."); } }, $browser_object = browser_object_links); define_key(default_global_keymap, "C-x i", "instapaper"); define_key(default_global_keymap, "C-x I", "instapaper-link");
7.5. Post to Read It Later
Similar to the one above, but for Read It Later instead.
interactive("ril", "Read it Later", function (I) { let posturl = 'https://readitlaterlist.com/v2/add?username=USERNAME&password=PASSWORD&apikey=0Z4A9y66d8cF2fMu42p9b46Zb8T2vqNq&url=' + I.buffer.display_uri_string + '&title=' + I.buffer.document.title; yield send_http_request(load_spec({uri: posturl})); I.window.minibuffer.message("Saved!"); }); define_key(default_global_keymap, "C-r", "ril"); interactive("open-ril", "Go to Read It Later website", "follow", $browser_object = "http://www.readitlaterlist.com/unread"); define_key(default_global_keymap, "C-x r", "open-ril");
Replace USERNAME with your username and PASSWORD with password. To use, simply type C-r. To go to your Read It Later unread list, type in C-x r, or, for a new buffer, C-u C-x r. I need to figure out the whole error handling thing, but it works for now
7.6. Delicious
Since moving from firefox to conkeror (great!), i haven't really used bookmarks because i don't know how to import and i asked myself what if i switch browsers or computer soon? Thus i decided to use delicious with conkeror. Note that modules/webjumps.js does include webjumps for use with delicious, but i wanted a tighter integration. Put the following in your .conkerorrc file:
interactive("delicious-post", "bookmark the page via delicious", function (I) { check_buffer(I.buffer, content_buffer); let posturl = 'https://api.del.icio.us/v1/posts/add?&url=' + encodeURIComponent( load_spec_uri_string( load_spec(I.buffer.top_frame))) + '&description=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "name (required): ", $initial_value = I.buffer.title)) + '&tags=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "tags (space delimited): ")) + '&extended=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "extended description: ")); try { var content = yield send_http_request( load_spec({uri: posturl})); I.window.minibuffer.message(content.responseText); } catch (e) { } }); interactive("delicious-post-link", "bookmark the link via delicious", function (I) { bo = yield read_browser_object(I) ; mylink = load_spec_uri_string( load_spec(encodeURIComponent(bo))); check_buffer(I.buffer, content_buffer); let postlinkurl = 'https://api.del.icio.us/v1/posts/add?&url=' + mylink + '&description=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "name (required): ", $initial_value = bo.textContent)) + '&tags=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "tags (space delimited): ")) + '&extended=' + encodeURIComponent( yield I.minibuffer.read( $prompt = "extended description: ")); try { var content = yield send_http_request( load_spec({uri: postlinkurl})); I.window.minibuffer.message(content.responseText); } catch (e) { } }, $browser_object = browser_object_links); define_key(default_global_keymap, "p", "delicious-post"); define_key(default_global_keymap, "P", "delicious-post-link"); define_webjump("del", "http://delicious.com/search?p=%s&chk=&context=userposts%7CYOUR_USERNAME_RIGHT_HERE&fr=del_icio_us&lc=");
Change YOUR_USERNAME_RIGHT_HERE to your username.
NOTE:
you can modify the above code easily if you want to include more fields. For example appending '&replace=no&shared=no' to posturl or postlinkurl would make the bookmark private and won't replace if the bookmake already exists. See Delicious's api specs for more details: http://delicious.com/help/api.
This will not work with yahoo id authenticated logins. We are supposed to use OAuth for it, which is explained here http://delicious.com/help/oauthapi but not yet done here.
- Now, while surfing, you can hit "p" to bookmark the page, and type in the name, tags, and extended description in conkeror. If you want to bookmark a link, hit "P" [It'd be nice to make the suggested name be the words that is linked-afied or the name of the page]. When bookmarking for the first time in a session, delicious will ask for your username and password. Just type them in and save. Also, after bookmarking, look for a "done" message to know the bookmark works. Otherwise, you will see a "something went wrong" from delicious.
Use the webjump del to search for tags. Ideally, it'd be nice if we can use a google-like webjump, where links appear in the minibuffer for us to select. However, I don't know how to do this. Not sure if it works, but see this mail http://www.mail-archive.com/conkeror@mozdev.org/msg01721.html
7.7. Google Bookmarks
Here is a .conkerorrc.js script adding basic Google Bookmark integration to Conkeror. Use 'p' to open a Google Bookmark in the current buffer, 'C-u p' for a new buffer and 'C-u C-u p' for a background buffer. Stuff missing:
- Caching
- A way to add a bookmark from conkeror
- Login functionality (currently depends on a Google Bookmarks session cookie being present, just login to Google Bookmarks once, I guess)
Contributions are very welcome.
/** * 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/>. */ function assert(e) { if(!e) throw "Assertion Failure!"; } function elementText(el) { assert(el.nodeType == el.ELEMENT_NODE); var childNodes = el.childNodes; assert(childNodes.length == 1); var textChild = childNodes[0]; assert(textChild.nodeType == textChild.TEXT_NODE); return textChild.nodeValue; } function childElements(el) { var result = new Array(); assert(el.nodeType == el.ELEMENT_NODE); var childNodes = el.childNodes; for (let i = 0; i < childNodes.length; ++i) { let child = childNodes[i]; if (child.nodeType == child.ELEMENT_NODE) { result.push(child); } } return result; } function searchBookmarks(query) { var bookmarksURL = 'http://www.google.com/bookmarks/find?output=xml&q=' + escape(query); dump("getting bookmarks from: '" + bookmarksURL + "'. "); var result = yield getBookmarks(bookmarksURL); dump(result.length + " results\n"); yield co_return( result ); } function getBookmarks(bookmarksURL) { var httpresp = yield send_http_request(load_spec({uri:bookmarksURL})); var result = parseBookmarks(httpresp.responseText); yield co_return(result); } function parseBookmarks(xml){ var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"] .createInstance(Components.interfaces.nsIDOMParser); var dom = parser.parseFromString(xml, "text/xml"); return parseBookmarksDoc(dom.documentElement); } function parseBookmarksDoc(doc) { var result = new Array(); var bookmarkels = doc.getElementsByTagName('bookmark'); for (let i = 0; i < bookmarkels.length; ++i) { let bookmarkel = bookmarkels[i]; result.push(parseBookmarkElement(bookmarkel)); } return result; } function parseBookmarkElement(bookmarkel) { var title; var url; var timestamp; var id; var labels; var attributes; var children = childElements(bookmarkel); for (let i in children) { var childel = children[i]; switch(childel.nodeName) { case 'title': title = elementText(childel); break; case 'url': url = elementText(childel); break; case 'timestamp': timestamp = elementText(childel); break; case 'id': id = elementText(childel); break; case 'labels': labels = parseListElement(childel,'label'); break; case 'attribute': attributes = parseListElement(childel,'attribute'); break; } } return { title: title, url: url, labels: labels, id: id, timestamp: timestamp }; } function parseListElement(parentel, expectedNodeName) { var result = new Array(); var children = childElements(parentel); for (let i in children) { let childel = children[i]; if(childel.nodeName == expectedNodeName) { result.push(elementText(childel)); } } return result; } function google_bookmark_completer(input, pos, conservative) { var bookmarks = yield searchBookmarks(input); var titles = new Array(); for (let i in bookmarks) { titles.push(bookmarks[i].title); } yield co_return({ count: bookmarks.length, indexOf: function(x) { return titles.indexOf(x); }, get_string: function(i) { return bookmarks[i].url; }, get_description: function(i) { return bookmarks[i].title; }, get_input_state: function(i) { return [bookmarks[i].title]; }, get_value: function(i) {return bookmarks[i];} }); } function goto_google_bookmark(I, loadfun) { var title = yield I.minibuffer.read( $prompt = 'Go to Google Bookmark:', $history = 'google-bookmark-queries', $completer = google_bookmark_completer ); var bms = yield searchBookmarks(title); var url = bms[0].url; loadfun(I,url); } function goto_google_bookmark_current_buffer(I) { yield goto_google_bookmark(I, function(I,url) { I.buffer.load(url); }); } function goto_google_bookmark_new_buffer(I) { yield goto_google_bookmark_new_buffer_target(I,OPEN_NEW_BUFFER); } function goto_google_bookmark_new_buffer_target(I,target) { yield goto_google_bookmark(I, function(I,url) { create_buffer(I.buffer.window, buffer_creator(content_buffer, $opener = I.buffer, $load = load_spec({uri:url})), target); }); } function goto_google_bookmark_new_window(I) { yield goto_google_bookmark_new_buffer_target(I,OPEN_NEW_WINDOW); } function goto_google_bookmark_new_buffer_background(I) { yield goto_google_bookmark_new_buffer_target(I,OPEN_NEW_BUFFER_BACKGROUND); } interactive('goto-google-bookmark', "Queries the title of a Google Bookmark (with completion) and opens it.", alternates( goto_google_bookmark_current_buffer, goto_google_bookmark_new_buffer, goto_google_bookmark_new_buffer_background )); define_key(content_buffer_normal_keymap, 'p', 'goto-google-bookmark');
7.8. Google Search Results
7.8.1. Cleaning Redirecting Search Links
Conkeror page mode cleaning redirecting search links:
https://github.com/intuser/google-links
Please note, that this only works if you change the useragent as explained in the Readme. Also note, that the script does not provide a mechanism to revert its effect.
7.8.2. Conkeror Keys on Google Search Pages
Enable the conkeror keys on google search pages:
https://github.com/intuser/google-keys
Please note, that the script does not provide a mechanism to revert its effect.
7.9. Posting to Bibsonomy
The following snippet lets you post easily to http://bibsonomy.org :
interactive("bibsonomy-post-publication", "Post a publication to Bibsonomy. Either uses the URL and scrapes the page, or sends the selected bibtex.", function (I) { var element = yield read_browser_object(I); var spec = load_spec(element); newspec = 'http://www.bibsonomy.org/BibtexHandler?requTask=upload&url='+encodeURIComponent(load_spec_uri_string(spec))+'&description='+encodeURIComponent(load_spec_title(spec))+'&selection='+encodeURIComponent(I.buffer.top_frame.getSelection()); browser_object_follow(I.buffer, OPEN_CURRENT_BUFFER, newspec); }, $browser_object = browser_object_frames); define_key(content_buffer_normal_keymap, "C-c b", "bibsonomy-post-publication");
7.10. TinyURL
The following code makes a browser-object class for a tiny-url of the page you are currently browsing. It binds * q to the browser object, so to put a tinyurl on the clipboard, you would use the sequence * q c.
// last updated September 22, 2009 define_browser_object_class( "tinyurl", "Get a tinyurl for the current page", function (I, prompt) { check_buffer(I.buffer, content_buffer); let createurl = 'http://tinyurl.com/api-create.php?url=' + encodeURIComponent( load_spec_uri_string( load_spec(I.buffer.top_frame))); try { var content = yield send_http_request( load_spec({uri: createurl})); yield co_return(content.responseText); } catch (e) { } }); define_key(content_buffer_normal_keymap, "* q", "browser-object-tinyurl");
Now to whom do I apply for the "bonus points" mentioned by the original author of this tip? --retroj
7.11. Youtube
A piece of code to take video and audio from youtube url. Needs ffmpeg and wget to work, default recording path is /tmp .
Out of date as of 2013-02-15 Problem looks like video URL regexp.
var Ytu = { I: null, url: null, videoFilename: null, audioFilename: null, init : function(I) { Ytu.I = I; Ytu.url = load_spec_uri_string(load_spec(I.buffer.top_frame)); }, setPath : function(path) { Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("ytu-recorder.").setCharPref('path', path); }, getPath : function() { let prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("ytu-recorder."); let path = "/tmp"; if(prefs.prefHasUserValue('path')){ path = Components.classes["@mozilla.org/preferences-service;1"].getService(Components.interfaces.nsIPrefService).getBranch("ytu-recorder.").getCharPref('path'); } return path; }, getVideoUrl : function() { let videoUrl = null; if(!(new RegExp("http://(?:www\\.)?youtube\\.\\w{2,3}/watch\\?v\\=[a-z0-9A-Z\-_]{11,}(?:\&feature\=related)?", "")).test(Ytu.url)) { throw "It's not a valid youtube url"; } let videoUrlReg = new RegExp('"([^"]+generate_204[^"]+)"', ""); if(videoUrlReg.test(Ytu.I.buffer.document.getElementsByTagName('html').item(0).innerHTML.match(videoUrlReg))) { videoUrl = Ytu.I.buffer.document.getElementsByTagName('html').item(0).innerHTML.match(videoUrlReg)[1].replace('generate_204', 'videoplayback').replace(/\\/g,'').replace(/u0026/g,'&'); } else { throw "No video found"; } return videoUrl; }, getTitle : function() { let title = 'no title'; let metas = Ytu.I.buffer.document.getElementsByTagName('meta'); for (let i = 0; i < metas.length; i++) { if(metas.item(i).getAttribute('name') == 'title') { title = metas.item(i).getAttribute('content'); } } title = encodeURI(title).replace(/%20/g, ' '); return title; }, getCmdToDownloadVideo : function() { return "wget -U 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.12 Safari/535.11' -O '"+Ytu.videoFilename+"' '"+Ytu.getVideoUrl()+"'"; }, getCmdToExtractSound : function() { return "ffmpeg -i '"+Ytu.videoFilename+"' -ac 2 -ab 196 -vn -y '"+Ytu.audioFilename+"'"; }, downloadVideo : function() { Ytu.videoFilename = Ytu.getPath()+"/"+Ytu.getTitle()+".flv"; Ytu.I.window.minibuffer.message('Video downloading started : file would be recorded in '+Ytu.getPath()+' with name '+Ytu.getTitle()); shell_command_blind(Ytu.getCmdToDownloadVideo()); }, extractSound : function() { Ytu.videoFilename = "/tmp/"+Ytu.getTitle()+".flv"; Ytu.audioFilename = Ytu.getPath()+"/"+Ytu.getTitle()+".mp3"; Ytu.I.window.minibuffer.message('Sound extracting started : file would be recorded in '+Ytu.getPath()+' with name '+Ytu.getTitle()); shell_command_blind(Ytu.getCmdToDownloadVideo()+" && "+Ytu.getCmdToExtractSound()+" && rm '"+Ytu.videoFilename+"'"); }, extractSoundAndDownloadVideo : function() { Ytu.videoFilename = Ytu.getPath()+"/"+Ytu.getTitle()+".flv"; Ytu.audioFilename = Ytu.getPath()+"/"+Ytu.getTitle()+".mp3"; Ytu.I.window.minibuffer.message('Sound extracting and video downloading started : files would be recorded in '+Ytu.getPath()+' with name '+Ytu.getTitle()); shell_command_blind(Ytu.getCmdToDownloadVideo()+" && "+Ytu.getCmdToExtractSound()); } }; interactive("ytu-download-video", "extract video from youtube video", function(I){ Ytu.init(I); Ytu.downloadVideo(); }); interactive("ytu-extract-sound", "extract sound from youtube video", function(I){ Ytu.init(I); Ytu.extractSound(); }); interactive("ytu-extract-sound-and-download-video", "extract sound and video from youtube video", function(I){ Ytu.init(I); Ytu.extractSoundAndDownloadVideo(); }); interactive("ytu-set-path", "set extracting path", function(I){ let path = yield I.minibuffer.read($prompt = "Path to use to record sound and video : "); Ytu.setPath(path); }); define_key(content_buffer_normal_keymap, "y p", "ytu-set-path"); define_key(content_buffer_normal_keymap, "y s", "ytu-extract-sound"); define_key(content_buffer_normal_keymap, "y v", "ytu-download-video"); define_key(content_buffer_normal_keymap, "y b", "ytu-extract-sound-and-download-video");
7.12. Batoto
This makes the relationship-next and relationship-previous browser objects work on the Batoto manga reader site, enabling the ]] and [[ keyboard shortcuts for next/previous page.
function batoto_find_next (doc) { links = doc.getElementsByTagName("a"); for (i = 0; i < links.length; i++) { sub = links[i].firstChild; if (sub && sub.nodeType == 1 && sub.nodeName.toLowerCase() == "img" && sub.attributes['src'].value == "http://www.batoto.net/images/next.png") return links[i]; } } function batoto_find_prev (doc) { links = doc.getElementsByTagName("a"); for (i = 0; i < links.length; i++) { sub = links[i].firstChild; if (sub && sub.nodeType == 1 && sub.nodeName.toLowerCase() == "img" && sub.attributes['src'].value == "http://www.batoto.net/images/prev.png") return links[i]; } } define_page_mode("batoto-mode", build_url_regexp($domain = "batoto", $allow_www = true, $tlds = ["net"]), function enable (buffer) { buffer.page.local.browser_relationship_patterns = {}; buffer.page.local.browser_relationship_patterns[RELATIONSHIP_NEXT] = [batoto_find_next]; buffer.page.local.browser_relationship_patterns[RELATIONSHIP_PREVIOUS] = [batoto_find_prev]; }, function disable (buffer) { }, $display_name = "Batoto"); page_mode_activate(batoto_mode);
8. Interaction with Other Programs
8.1. Set Emacs' Default Browser to Conkeror
(setq browse-url-browser-function 'browse-url-generic browse-url-generic-program "/path/to/conkeror")
8.2. Bind a Key in Emacs's Dired-Mode to View a File in the Default Browser
(add-hook 'dired-mode-hook (lambda () (define-key dired-mode-map "b" 'my-browser-find-file))) (defun my-dired-browser-find-file () "Dired function to view a file in a web browser" (interactive) (browse-url (browse-url-file-url (dired-get-filename))))
8.3. Org-Remember-Mode Integration
8.3.1. Remember the current url with org-remember
http://tsdh.wordpress.com/2008/11/14/calling-org-remember-from-inside-conkeror/
(With recent versions of org-mode it is best to use the code under "UPDATE: Now I use org-protocol instead of something home-brewn.")
8.3.2. Save and Remember with Org-Mode
https://github.com/intuser/org-save
There you find a script which provides two commands to save and capture with org-mode at one go. It also adds key-bindings for these commands. One command saves the page or a link, the other the page with all its supporting documents.
8.4. Export Firefox Keyword Searches as Conkeror Webjumps
Locate your Firefox's profile directory
cd to that directory and then do sqlite3 places.sqlite (this is assuming you have sqlite3 installed)
- when you have a sqlite prompt, run this sql
select keyword,url from moz_keywords left join moz_bookmarks on (moz_keywords.id = keyword_id) left join moz_places on (fk = moz_places.id);
- you will get a list of your firefox search keywords and urls
acpan|http://www.annocpan.org/?mode=search;field=Module;latest=1;name=%s cpan|http://search.cpan.org/search?query=%s&mode=module ar|http://web.archive.org/web/*/%s
You can now use your favourite text processing system to generate the webjump definitions. Here is how I used perl to generate the webjumps (no line breaks):
sqlite3 -list places.sqlite " select keyword,url from moz_keywords left join moz_bookmarks on (moz_keywords.id = keyword_id) left join moz_places on (fk = moz_places.id)" | perl -lane '($k,$u)=split/\|/;print "define_webjump(\"$k\", \"$u\");"'
This gave me:
define_webjump("acpan", "http://www.annocpan.org/?mode=search;field=Module;latest=1;name=%s"); define_webjump("cpan", "http://search.cpan.org/search?query=%s&mode=module"); define_webjump("ar", "http://web.archive.org/web/*/%s");
8.5. Using an external password manager
This code lets you call an external password manager for saving passwords. The recommended usage is to place separate very strong passwords (30-character true-random all-alphanumeric is good) which are impossible to crack, even with advanced techniques and offline hash-file access, but very hard to remember in the manager; this grants security against brute-force and password reuse attacks, while not requiring eidetic memory. The disadvantage, of course, is that you can only log in to services in the manager from the computer on which the manager runs.
function manage_do_get(elem, str) { var out = ""; var result = yield shell_command("/path/to/script.sh " + str, $fds=[{output: async_binary_string_writer("")}, {input: async_binary_reader(function (s) out += s || "") }]); elem.value = out; } interactive("manage-get", "Get a password from the password manager", function (I) { var n = I.buffer.focused_element; yield manage_do_get(n, (yield I.minibuffer.read($prompt = "password: "))); browser_element_focus(I.buffer, n); }); define_key(content_buffer_normal_keymap, "C-x p", "manage-get");
The script it calls should take an account name as its first argument, and return a password on stdout. An example follows:
\#!/bin/bash if [[ -f "$HOME/.gpg-agent" ]]; then source $HOME/.gpg-agent; export GPG_AGENT_INFO; fi keyringer keyring decrypt-batch $1
(keyringer is a thin wrapper around GPG; security in this instance is handled by using gpg-agent and pinentry-gtk, which allow you to enter your passphrase into the decryptor. Setting this up is left as an exercise for the reader.)
8.6. Copy Selection to Emacs kill ring
This requires an emacs daemon running.
interactive( "ekr_cmd_copy", "Copy the selection to the clipboard and the Emacs kill ring", function (I) { call_interactively(I, "cmd_copy") var cc = read_from_x_primary_selection(); cc = cc.replace(/([^\\]*)\\([^\\]*)/g, "$1\\\\$2"); cc = cc.replace('"', '\\"', "g"); cc = cc.replace("'", "'\\''", "g"); var ecc = "emacsclient -e '(kill-new \"" + cc + "\")' > /dev/null"; shell_command_blind(ecc); } ); undefine_key(caret_keymap,"M-w"); define_key(caret_keymap,"M-w", "ekr_cmd_copy"); undefine_key(content_buffer_normal_keymap,"M-w"); define_key(content_buffer_normal_keymap,"M-w", "ekr_cmd_copy"); undefine_key(special_buffer_keymap,"M-w"); define_key(special_buffer_keymap,"M-w", "ekr_cmd_copy"); undefine_key(text_keymap,"M-w"); define_key(text_keymap,"M-w", "ekr_cmd_copy");
9. Browsing through Tor
NOTE: As it is unlikely that Torbutton is compatible with Conkeror, do not use Conkeror with Tor if you require strong anonymity. Without Torbutton, malicious pages and Tor exit nodes can force Mozilla to leak information about the user and the system. Conkeror probably won't leak personal information after disabling cookies, Javascript, and Java, however it is better to err on the side of caution by browing with Tor using Torbutton and the recommended version of Firefox.
Tor needs a web proxy like Polipo or Privoxy. See Tor's documentation to learn how to set one up.
9.1. Create a new profile for Tor
Open the profile manager to create a new profile.
$ conkeror -no-remote -ProfileManager
Run conkeror using e.g. the "tor" profile.
$ conkeror -no-remote -P tor
It helps to have the profile name in the window title. See Profiles for instructions.
9.2. Proxy settings
XXX: this section advises the use of prefs.js in the profile, but the preferred way in conkeror is to use session_pref and user_pref the rc. use session_pref where possible, and user_pref where necessary. fixme.
Add the following to ~/.conkeror.mozdev.org/conkeror/<profile>/prefs.js:
user_pref('network.proxy.http', "localhost"); user_pref('network.proxy.http_port', 8118); user_pref('network.proxy.ssl', "localhost"); user_pref('network.proxy.ssl_port', 8118); user_pref('network.proxy.socks', "localhost"); user_pref('network.proxy.socks_port', 9050); user_pref('network.proxy.type', 1); pref("network.http.keep-alive", false); pref("network.http.max-persistent-connections-per-proxy", 0); pref("network.http.max-persistent-connections-per-server",i 0);
9.3. Disable Cookies, Javascript, and Java
Forthcoming...
9.4. Test
Run conkeror under e.g. the "tor" profile.
$ conkeror -no-remote -P tor http://check.torproject.org
10. Development Tools
10.1. Firebug Lite
The following code provides a firebug command which will launch Firebug Lite in the current page. To improve load time, and/or use Firebug Lite offline, refer to the section Using Firebug Lite Offline at http://getfirebug.com/firebuglite.
define_variable("firebug_url", "http://getfirebug.com/releases/lite/1.2/firebug-lite-compressed.js"); function firebug (I) { var doc = I.buffer.document; var script = doc.createElement('script'); script.setAttribute('type', 'text/javascript'); script.setAttribute('src', firebug_url); script.setAttribute('onload', 'firebug.init();'); doc.body.appendChild(script); } interactive("firebug", "open firebug lite", firebug);
10.2. MODI
The Mouseover DOM Inspector, or MODI for short, allows you to view and manipulate the DOM of a web page simply by mousing around the document. There are plenty of shortcuts with MODI. But being all capitals they rarely interfere with conkeror's binding. See the homepage for complete documentation http://slayeroffice.com/tools/modi/v2.0/modi_help.html.
interactive("modi", "The Mouseover DOM Inspector, or MODI for short, is a favelet (also known as a bookmarklet)" + " that allows you to view and manipulate the DOM of a web page simply " + "by mousing around the document (http://slayeroffice.com/tools/modi/v2.0/modi_help.html).", function(I) { z=I.buffer.document.body.appendChild(I.buffer.document.createElement('script')); z.language='javascript'; z.type='text/javascript'; z.src='http://slayeroffice.com/tools/modi/v2.0/modi_v2.0.js'; z.id='modi'; });
10.3. Examine element attributes through the minibuffer
With the following command, you can select any element in the page through the usual hinting system and get a list of its HTML and CSS attributes in the minibuffer, complete with their current values. The command uses the minibuffer's autocompletion mechanism to allow the user to browse through the list or search for a particular attribute. Hitting enter to select the attribute terminates the command.
// Examine element properties and style. interactive("examine-element", "Examine the attributes and style of a DOM node.", function print_attribute (I) { var element = yield read_browser_object(I); var list = []; var style = I.window.getComputedStyle(element); var attributes = element.attributes; var name = element.tagName.toLowerCase(); if (element.id) { name += "#" + element.id; } for (i = 0 ; i < element.classList.length ; i += 1) { name += "." + element.classList.item(i); } for (i = 0 ; i < style.length ; i += 1) { list.push([style.item(i), 1]); } for (i = 0 ; i < attributes.length ; i += 1) { list.push([attributes.item(i).name, 2]); } yield I.minibuffer.read( $prompt = name + ":", $completer = new prefix_completer( $completions = list.sort(), $get_string = function(item) item[0], $get_description = function(item) { var s, value; switch(item[1]) { case 1: s = "CSS property"; value = style.getPropertyValue(item[0]); break; case 2: s = "Attribute"; value = element.getAttribute(item[0]); break; } if (value) { s += " with value " + value; } return s; }), $auto_complete = true, $auto_complete_initial = true, $auto_complete_delay = 0, $require_match = false); }, $browser_object = browser_object_dom_node); define_key(content_buffer_normal_keymap, "x", "examine-element");
11. Automation
11.1. Reload pages at a certain interval
The following snippet will expose M-x interval-reload which takes a parameter from the minibuffer. The parameter is the number of minutes:seconds between reloads of the page. This only reloads the current buffer and also adds a hook to remove the interval after the buffer is killed.
var ref; interactive("interval-reload", "Reload current buffer every n minutes", function (I) { var b = I.buffer; var i = yield I.minibuffer.read($prompt="Interval (mm:ss)?"); if (i.indexOf(":") != -1) { mmss = i.split(":"); i = ((parseInt(mmss[0]) * 60) + parseInt(mmss[1])) * 1000; } else { i = parseInt(i) * 1000; } ref = call_at_interval(function () { reload(b); }, i); add_hook.call(b, "kill_buffer_hook", function() { ref.cancel(); }); });
This second half allows you to cancel any already running intervals.
interactive("cancel-intervals", "Cancel all running interval reloads", function (I) { ref.cancel(); });