Skip to content

Instantly share code, notes, and snippets.

@sadasant
Last active August 29, 2015 14:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sadasant/1d51bd8d26b5fd94f9f3 to your computer and use it in GitHub Desktop.
Save sadasant/1d51bd8d26b5fd94f9f3 to your computer and use it in GitHub Desktop.
Reading fast bookmarklet
// Bookmark url (remember to update the https link inside it for your fork):
// javascript:(function(){var d=document,s=d.createElement('script');s.src='https://cdn.rawgit.com/sadasant/1d51bd8d26b5fd94f9f3/raw/read.js';d.body.appendChild(s);}())
if (window.READING) {
alert("READ is already available :)");
} else {
(function() {
var READING = window.READING = {};
READING.delay = 100;
READING.extra = 100;
READING.top = 100;
READING.long = 9;
READING.started = false;
READING.stop = false;
var invalid = {
parents: ["A","B","EM","I", "#text"],
siblings: ["TABLE"]
};
// Geting the slected text
function getSelected() {
var text = "";
var selection;
if (window.getSelection) {
selection = window.getSelection();
text = selection.toString();
} else if (document.selection && document.selection.type != "Control") {
selection = document.selection.createRange();
text = selection.text;
}
return {
text: text,
selection: selection
};
}
// Getting the current paragraph
function getParagraph() {
var p = getSelected().selection.anchorNode;
while(invalid.parents.indexOf(p.nodeName) >= 0) {
p = p.parentNode;
}
return p;
}
// Scrolling to the given element
function scrollTo(element) {
element.scrollIntoView();
window.scrollTo(window.scrollX, window.scrollY - READING.top);
}
// Selecting the paragraph where the current selection is located
function selectTextIn(element) {
var range;
if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(element);
range.select();
} else {
range = document.createRange();
range.selectNodeContents(element);
var selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
}
}
// Gets the next sibling
function nextSibling(el) {
while(true) {
if (!el.nextSibling) {
el = el.parentNode;
}
el = el.nextSibling;
if (el.innerText === "") continue;
if (invalid.siblings.indexOf(el.nodeName) >= 0) continue;
if (el.nodeType == 1) break;
}
return el;
}
// Gets the previous sibling
function prevSibling(el) {
while(true) {
if (!el.previousSibling) {
el = el.parentNode;
}
el = el.previousSibling;
if (el.innerText === "") continue;
if (invalid.siblings.indexOf(el.nodeName) >= 0) continue;
if (el.nodeType == 1) break;
}
return el;
}
// Creating the reading widget
function createWidget() {
var div = document.createElement('div');
var txt = document.createElement('div');
div.style.position = "fixed";
div.style.textAlign = "center";
div.style.top = "0px";
div.style.left = "0px";
div.style.width = "100%";
div.style.height = "100%";
div.style.background = "rgba(0,0,0,.9)";
div.style.zIndex = "999999999";
txt.style.color = "white";
txt.style.marginTop = READING.top + "px";
txt.style.fontSize = "32px";
txt.style.fontFamily = "arial";
div.appendChild(txt);
document.body.appendChild(div);
return {
div: div,
txt: txt
};
}
// Read creates the widget and starts reading the selected text
function read() {
if (READING.started) {
READING.stop = true;
return;
}
var text = getSelected().text;
if (!text) return;
function prepare() {
var words = [""];
var valid = /[\(\)\[\]\{\}¿?¡!""''“”‘’0-9a-zA-Z\u00C0-\u017F]/; // The range \u00C0-\u017F is for accents.
text.split("").forEach(function(char, i, chars) {
words[words.length - 1] += char;
if (!char.match(valid)) {
words[words.length - 1].trim();
words.push("");
}
if (i === chars.length - 1) {
start(words);
}
});
}
function start(words) {
READING.started = true;
var w = createWidget();
console.log("READING", words);
function next() {
if (READING.stop || !words.length) {
READING.stop = false;
READING.started = false;
w.div.remove();
console.log("READING STOPPED");
return;
}
var word = words.shift();
w.txt.innerText = word;
// Delaying the next call
// because of certain characters
// or word length
var EXTRA = 0;
if (word) {
if (word.slice(-2).match(/[,.:?!]/)) {
EXTRA = READING.extra;
}
if (word.length > READING.long) {
EXTRA = Math.floor(word.length/READING.long) * READING.extra;
}
}
setTimeout(next, READING.delay + EXTRA);
}
setTimeout(next, READING.delay);
}
console.log("READING", text);
prepare();
}
// Catching keys
function catchKeys() {
var keydown;
if (window.onkeydown) {
keydown = window.onkeydown;
}
window.onkeydown = function(e) {
if (e.ctrlKey) return keydown && keydown();
// shift+r: start reading
if (e.shiftKey && e.keyCode === 82) return read();
// ESC: stop reading
if (READING.started && e.keyCode === 27) {
READING.stop = true;
}
var p = getParagraph();
// shift+s: Select the whole paragraph.
if (e.shiftKey && e.keyCode === 83) {
selectTextIn(p);
}
// shift+n: Select the next paragraph.
if (e.shiftKey && e.keyCode === 78) {
p = nextSibling(p);
scrollTo(p);
selectTextIn(p);
}
// shift+p: Select the previous paragraph.
if (e.shiftKey && e.keyCode === 80) {
p = prevSibling(p);
scrollTo(p);
selectTextIn(p);
}
// shift+d: Change the delay until the next word.
if (e.shiftKey && e.keyCode === 68) {
READING.delay = parseInt(prompt("Delay until the next word:", READING.delay), 10);
READING.extra = parseInt(prompt("Extra delay for big words:", READING.extra), 10);
}
if (keydown) keydown();
};
}
catchKeys();
alert([
"READ: Now reading!",
"Available commands:",
"shift+r: Start reading.",
"ESC: Stop reading.",
"shift+s: Select the whole paragraph.",
"shift+n: Select the next paragraph.",
"shift+p: Select the previous paragraph.",
"shift+d: Change the delay until the next word."
].join("\n"));
})();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment