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 RET 0 RET

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

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

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.4. 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.5. 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);
}

2.6. Switch to recent buffer (Alt-Tab like behavior)

To emulate the Alt-Tab behavior in usual OS put the following code in your .conkerorrc (from http://article.gmane.org/gmane.comp.mozilla.conkeror/2039 ). The interface is the same as with switch-to-buffer (C-x b) but the buffers are sorted in decreasing time access order. The only difference to Alt-Tab (or Ctr-Tab in other browsers) is that you will have to press RET to access the buffer; but this is a small price to pay for the ability to narrow down the list of candidates.

minibuffer.prototype.read_recent_buffer = function () {
    var window = this.window;
    var buffer = this.window.buffers.current;
    keywords(arguments, $prompt = "Buffer:",
             $default = buffer,
             $history = "buffer");
    var buffers = window.buffers.buffer_list.slice(0);
    buffers.push(buffers.shift());
    var completer = all_word_completer(
        $completions = buffers,
        $get_string = function (x) x.title, 
        $get_description = function (x) x.description);
    var result = yield this.read(
        $keymap = read_buffer_keymap,
        $prompt = arguments.$prompt,
        $history = arguments.$history,
        $completer = completer,
        $match_required = true,
        $auto_complete = "buffer",
        $auto_complete_initial = true,
        $auto_complete_delay = 0,
        $default_completion = arguments.$default);
    yield co_return(result);
};

interactive("switch-to-recent-buffer",
            "Switch to a buffer specified in the minibuffer.  List of buffers "+
            "will be ordered by recency.",
            function (I) {
                switch_to_buffer(
                    I.window,
                    (yield I.minibuffer.read_recent_buffer(
                         $prompt = "Switch to buffer:",
                         $default = (I.window.buffers.count > 1 ?
                                     I.window.buffers.buffer_list[1] :
                                     I.buffer))));
            });

define_key(default_global_keymap, "C-x B", "switch-to-recent-buffer");
define_key(default_global_keymap, "C-tab", "switch-to-recent-buffer");
define_key(read_buffer_keymap, "C-tab", "minibuffer-complete");
define_key(read_buffer_keymap, "C-S-tab", "minibuffer-complete-previous");

My personal preference, is to use M-8 and M-* instead of C-tab and C-S-tab above; in this way, the hands are always in typing position, which speeds up narrowing.

3. Look and Feel

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

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

3.3. Hide Scroll Bars

function disable_scrollbars (buffer) {
    buffer.browser.contentWindow.scrollbars.visible = false;
}
add_hook ("content_buffer_location_change_hook", disable_scrollbars);

This tip has been known to break isearch's scrolling functionality, as well as the functionality of mouse scroll wheels. There is no known solution at this time.

3.4. Default Zoom Level

function my_zoom_set (buffer) {
    call_after_timeout(function () {
        browser_zoom_set(buffer, false, 150);
    }, 0);
}
add_hook('create_buffer_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 ];

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

3.6. Make the current page readable by removing clutter

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

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

3.8. 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 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. (note: you can set these options too via about:config at runtime. changes will be automaticaly saved to the mentoined file)

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

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/.cocnkeror.css');

To toggle the style sheet's' active state:

var global_css_registered=true;
register_user_stylesheet('file:///home/$USER/.cocnkeror.css');
function toggle_global_css(I){
    global_css_registered=global_css_registered ? false : true;
    if(global_css_registered){
        register_user_stylesheet('file:///home/$USER/.cocnkeror.css');
    }else{
        unregister_user_stylesheet('file:///home/$USER/.cocnkeror.css');
    }
}
interactive("toggle-global-css", "Toggle global.css", toggle_global_css);
define_key(default_global_keymap, "C-t", "toggle-global-css");

4. User Preferences

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

4.2. Manipulate 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. Downloads

5.1. Default paths and filename transformations for downloads

Out of date information. TO-DO: update.

function suggest_save_path_from_file_name(file_name, buffer) {
    var cwd = (buffer && buffer.cwd) || default_directory.path;

    var file = make_file(cwd);
    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.

5.2. Remember the last save directory for downloads

Add the following code to your rc:

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

6. Interaction With the Third-Party Sites

6.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/preview/*/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/preview/*/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");

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

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

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

6.5. Integrate delicious with conkeror

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

6.6. Integrate Google Bookmarks with Conkeror

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

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

6.8. Create a TinyURL for the Current Buffer's URL

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. Interaction with Other Programs

7.1. Set Emacs' Default Browser to Conkeror

(setq browse-url-browser-function 'browse-url-generic
      browse-url-generic-program "/path/to/conkeror")

7.2. Org-Remember-Mode Integration

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

7.3. Bind to Multimedia Keys

This section concerns using multimedia keys in Conkeror on Linux. From X, we will map multimedia keys to F13 through F20. From Conkeror, we will bind commands to F13 through F20.

1. Discover which key code your multimedia key sends to X.

2. Map the keycode to F13.

keycode 234 = F13

3. In conkeror, you can now bind commands to F13:

define_key(content_buffer_normal_keymap, 'f13', 'go-back');

Pressing the multimedia key would now go back one page.

For more information on mapping multimedia keys, see this howto.

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

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

8.2. Proxy settings

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

8.3. Disable Cookies, Javascript, and Java

Forthcoming...

8.4. Test

Run conkeror under e.g. the "tor" profile.

$ conkeror -no-remote -P tor http://check.torproject.org

9. Development Tools

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

9.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. Automation

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

interactive("interval-reload", "Reload current buffer every n minutes", function (I) {
    var b        = I.buffer;
    var interval = yield I.minibuffer.read($prompt="Interval?");

    var ref = call_at_interval(function () {
                  reload(b);
              }, parseInt(interval) * 60 * 1000);

    add_hook.call(b, "kill_buffer_hook", function() {
        ref.cancel();
    });
});

Tips (last edited 2010-09-09 11:47:57 by LoicdAnterroches)