I read a lot and I like keeping my reading notes in OPML; it’s great for the structured content of a book. Authors have Books, Books have Chapters, etc. I also like to highlight/markup my books.
After resisting for a long time, I broke down and bought some Kindle books that I read on two different computers (desktop & laptop) and an Android tablet. My highlighting addiction has been interrupted by reading Kindle books because it’s a pain to keep up my OPML reading notes while also highlighting across multiple devices. Amazon does a good job of syncing reading positions and highlights created on multiple devices, so that my personal notes (highlights) are eventually combined and kept in the cloud but there’s a headache also — I can’t export them out of Amazon and I want them in OPML.
What I ended up having to do to get my highlights (reading notes) into OPML was to copy the text out of the Kindle app on the PC and paste into the OPML Editor and then I went back and selected the same text and added a highlight — it was obnoxious. It also failed on Android where there isn’t an OPML Editor.
Enter Greasemonkey. I cooked up a script that adds two images to the top left of the Kindle page that shows my highlights (per book) one to capture the highlights and format as OPML (true markup that I could save to file and open in the OPML Editor) and one that formatted as plain text (indented that I could copy and paste directly into the OPML Editor).
Now I just highlight as much as I want across multiple devices and then eventually reformat using the Greasemonkey script in order to save my highlights as OPML from the Amazon Kindle website.
The Amazon page with the Greasemonkey inserted images:

The Amazon page formatted as OPML:

The Amazon page formatted as text:

The content in the OPML Editor:

Here’s the Greasemonkey Script:
// ==UserScript==
// @name Amazon: Kindle Highlights to OPML
// @namespace havagan.amazon.kindle
// @include https://kindle.amazon.com/work/*
// @include http://kindle.amazon.com/work/*
// ==/UserScript==
var $;
// Load jQuery library
(function(){
if (typeof unsafeWindow.jQuery == ‘undefined’) {
var GM_Head = document.getElementsByTagName(‘head’)[0] || document.documentElement,
GM_JQ = document.createElement(‘script’);
GM_JQ.src = ‘http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js’;
GM_JQ.type = ‘text/javascript’;
GM_JQ.async = true;
GM_Head.insertBefore(GM_JQ, GM_Head.firstChild);
}
GM_wait();
})();
// Check if jQuery is loaded
function GM_wait() {
if (typeof unsafeWindow.jQuery == ‘undefined’) {
window.setTimeout(GM_wait, 100);
} else {
$ = unsafeWindow.jQuery.noConflict(true);
doMain();
}
}
// All your GM code must be inside this function
function doMain() {
addOpmlButton();
addTextButton();
}
function addOpmlButton()
{
$(‘body’).append(“<div id=’createOpml’ style=’position:absolute; top:0px; left:0px; margin:25px;’><img id=’opmlLogo’ src=’http://static.opml.org/images/opml.gif’ /></div>”);
$(“img#opmlLogo”).click(function () {
createOpml(getBookAuthor(), getBookTitle(), getBookHighlights());
});
}
function addTextButton()
{
$(‘body’).append(“<div id=’createOpml’ style=’position:absolute; top:50px; left:0px; margin:25px;’><img id=’textLogo’ src=’http://www.havagan.com/images/text.gif’ /></div>”);
$(“img#textLogo”).click(function () {
createText(getBookAuthor(), getBookTitle(), getBookHighlights());
});
}
function createOpml(bookAuthor, bookTitle, bookHighlights)
{
// wipe the page
wipeContent();
// recreate the page
$(‘head’).append(‘<title>’ + bookTitle + ‘ (‘ + bookAuthor + ‘)</title>’);
$(‘body’).append(‘<pre>’);
$(‘body’).append(‘<?xml version=”1.0″ encoding=”ISO-8859-1″?></br>’);
$(‘body’).append(‘ <?xml-stylesheet type=”text/xsl” href=”http://havaganserver/opml/opml.xslt” version=”1.0″?></br>’);
$(‘body’).append(‘ <opml version=”2.0″></br>’);
$(‘body’).append(‘ <head></br>’);
$(‘body’).append(‘ <title>’ + bookTitle + ‘ (‘ + bookAuthor + ‘)</title></br>’);
$(‘body’).append(‘ <dateCreated></dateCreated></br>’);
$(‘body’).append(‘ <dateModified></dateModified></br>’);
$(‘body’).append(‘ <ownerName></ownerName></br>’);
$(‘body’).append(‘ <ownerEmail></ownerEmail></br>’);
$(‘body’).append(‘ <expansionState></expansionState></br>’);
$(‘body’).append(‘ <vertScrollState></vertScrollState></br>’);
$(‘body’).append(‘ <windowTop></windowTop></br>’);
$(‘body’).append(‘ <windowLeft></windowLeft></br>’);
$(‘body’).append(‘ <windowBottom></windowBottom></br>’);
$(‘body’).append(‘ <windowRight></windowRight></br>’);
$(‘body’).append(‘ </head></br>’);
$(‘body’).append(‘ <body></br>’);
// create author/title elements
$(‘body’).append(‘ <outline text=”‘ + bookAuthor + ‘”></br>’);
$(‘body’).append(‘ <outline text=”‘ + bookTitle + ‘”></br>’);
// append highlights.
$.each(bookHighlights, function(index, value) {
$(‘body’).append(‘ <outline text=”‘ + (index + 1) + “. ” + bookHighlights[index] + ‘” /></br>’);
});
// end highlights.
$(‘body’).append(‘ </outline></br>’);
$(‘body’).append(‘ </outline></br>’);
$(‘body’).append(‘ </body></br>’);
$(‘body’).append(‘ </opml></br>’);
$(‘body’).append(‘</pre>’);
}
function createText(bookAuthor, bookTitle, bookHighlights)
{
// wipe the page
wipeContent();
// recreate the page
// create author/title elements
$(‘head’).append(‘<title>’ + bookTitle + ‘ (‘ + bookAuthor + ‘)</title>’);
$(‘body’).append(‘<div style=”text-indent:0em;”>’ + bookAuthor + ‘</div>’);
$(‘body’).append(‘<div style=”text-indent:0em;”> ’ + bookTitle + ‘</div>’);
// append highlights here.
$.each(bookHighlights, function(index, value) {
$(‘body’).append(‘<div style=”text-indent:0em;”> ’ + (index + 1) + “. ” + bookHighlights[index] + ‘</div>’);
});
}
function getBookTitle()
{
return $(‘div.bookInfo div.title’).text().trim();
}
function getBookAuthor()
{
return $(‘div.bookInfo div.author’).text().trim().replace(“by “, “”);
}
function getBookHighlights()
{
var bookHighlights = new Array();
$(‘span.highlight’).each(function(idx, item) {
bookHighlights.push(item.innerHTML.replace(/(\r\n|\n|\r)/gm,”").replace(/(\”|\”)/gm, “\’”));
});
return bookHighlights;
}
function wipeContent()
{
// start with a blank canvas; rebuild on a clean foundation.
$(‘head’).empty();
$(‘body’).empty();
}