welcome: please sign in

No pages like "Tips"!

Clear message
location: Tips

Contents

  1. General Usage
    1. Select Current Page with Browser Object Commands
    2. Set Homepage to a File in the Home Directory
    3. Generate a PDF file
  2. Navigation
    1. Follow Links in a New Buffer with a One-Key Binding
    2. Keyboard Shortcuts for Often-Used Sites
    3. Open Multiple "Bookmarks" with One Key
    4. Navigate with Mouse Buttons
    5. Open Middle-Clicked Links in New Buffers
    6. Selection Searches
    7. Client Redirect
    8. Browse buffer session history
  3. GUI Function
    1. Bind Number Keys to Switch to Buffers 1-10
    2. Switch to previously open buffer
    3. Order buffers by last access time
    4. Url Remoting Multiple Targets
    5. Restore Killed Buffer Url
    6. Revive Buffer (Undo closed tab)
  4. Look and Feel
    1. Mode line buttons for basic browser control
    2. Big Hint Numbers
    3. Hide Scroll Bars
    4. Default Zoom Level
    5. Darken the current page
    6. Make the current page readable by removing clutter with Arc90's Readability
    7. Use Readable (a Readability competitor) without changing buffer location
    8. View the current page through InstaPaper's "Text View"
    9. Ask before closing the window
    10. Render the web page with default (custom) colors
    11. Auto-hide the Minibuffer
    12. Auto-hide the Minibuffer and Mode Line
    13. Auto-hide the minibuffer and print text to the minibuffer, alternative implementation
      1. Example usage
    14. Disable Gif Animation
  5. Environment
    1. Cache Settings
    2. Proxy Settings
    3. Default Charset
    4. Binding Unsupported Keys
  6. Downloads
    1. Default paths and filename transformations for downloads
    2. Remember the last save directory for downloads
  7. Specific Sites and Services
    1. Subscribe to Atom/RSS feeds (google reader)
    2. Dictionary Search
    3. Facebook Share
    4. Post to Instapaper
    5. Post to Read It Later
    6. Delicious
    7. Google Bookmarks
    8. Posting to Bibsonomy
    9. TinyURL
    10. Youtube
    11. Batoto
  8. Interaction with Other Programs
    1. Set Emacs' Default Browser to Conkeror
    2. Bind a Key in Emacs's Dired-Mode to View a File in the Default Browser
    3. Org-Remember-Mode Integration
    4. Export Firefox Keyword Searches as Conkeror Webjumps
    5. Using an external password manager
  9. Browsing through Tor
    1. Create a new profile for Tor
    2. Proxy settings
    3. Disable Cookies, Javascript, and Java
    4. Test
  10. Development Tools
    1. Firebug Lite
    2. MODI
  11. Automation
    1. Reload pages at a certain interval

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.

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

2. Navigation

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");

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);
}

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+" ");
                    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");

This is best used in conjunction with the setting:

minibuffer_read_url_select_initial = false;

See UserVariables.

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.

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.

conkeror-buttons.png

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:

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.

  1. 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.

  2. 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

  3. 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");

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 :D

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:

  1. 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.

  2. 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.

  3. 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.
  4. 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:

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. 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.9. 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.10. 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.11. 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

http://tsdh.wordpress.com/2008/11/14/calling-org-remember-from-inside-conkeror/

8.4. Export Firefox Keyword Searches as Conkeror Webjumps

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.)

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';
           });

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();
});