// spritz.js // A JavaScript Speed Reader // rich@gun.io // https://github.com/Miserlou/OpenSpritz // Please don't abuse this. var readability_token = '172b057cd7cfccf27b60a36f16b1acde12783893'; var diffbot_token = '2efef432c72b5a923408e04353c39a7c'; function create_spritz(){ spritz_loader = function() { //getURL("https://rawgithub.com/Miserlou/OpenSpritz/master/spritz.html", function(data){ //getURL("https://rawgithub.com/Miserlou/OpenSpritz/dev/spritz.html", function(data){ // This won't work in Firefox because an old bug and won't work in Chrome because of security stuff: //getURL("spritz.html", function(data){ //getURL("https://rawgithub.com/Miserlou/OpenSpritz/dev/spritz.html", function(data){ // RawGit's CDN usage: // "Since files are not refreshed after the first request, // it's best to use a specific tag or commit URL, not a branch URL." getURL("https://cdn.rawgit.com/Miserlou/OpenSpritz/9e92c605032be16c986ed699d68e0acd3534e6b1/spritz.html", function(data){ var spritzContainer = document.getElementById("spritz_container"); if (!spritzContainer) { var ele = document.createElement("div"); data = data.replace(/(\r\n|\n|\r)/gm,""); ele.innerHTML = data; document.body.insertBefore(ele, document.body.firstChild); document.getElementById("spritz_toggle").style.display = "none"; }; document.getElementById("spritz_selector").addEventListener("change", function(e) { clearTimeouts(); spritz(); }); }); }; spritz_loader(); } function getURL(url, callback) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function() { if (xmlhttp.readyState == 4 && xmlhttp.status == 200) { callback(xmlhttp.responseText); } } xmlhttp.open("GET", url, true); xmlhttp.send(); } function hide_spritz(){ document.getElementById("spritz_spacer").style.display = "none"; document.getElementById("spritz_container").style.display = "none"; document.getElementById("spritz_holder").style.display = "none"; } // Entry point to the beef. // Gets the WPM and the selected text, if any. function spritz(){ var wpm = parseInt(document.getElementById("spritz_selector").value, 10); if(wpm < 1){ return; } var selection = getSelectionText(); if(selection){ spritzify(selection); } else{ spritzifyURL(); } } // The meat! function spritzify(input){ var wpm = parseInt(document.getElementById("spritz_selector").value, 10); var ms_per_word = 60000/wpm; // Split on any spaces. var all_words = input.split(/\s+/); // The reader won't stop if the selection starts or ends with spaces if (all_words[0] == "") { all_words = all_words.slice(1, all_words.length); } if (all_words[all_words.length - 1] == "") { all_words = all_words.slice(0, all_words.length - 1); } var word = ''; var result = ''; // Preprocess words var temp_words = all_words.slice(0); // copy Array var t = 0; for (var i=0; i 8) && all_words[i].indexOf('.') == -1){ temp_words.splice(t+1, 0, all_words[i]); temp_words.splice(t+1, 0, all_words[i]); t++; t++; } // Add an additional space after punctuation. if(all_words[i].indexOf('.') != -1 || all_words[i].indexOf('!') != -1 || all_words[i].indexOf('?') != -1 || all_words[i].indexOf(':') != -1 || all_words[i].indexOf(';') != -1|| all_words[i].indexOf(')') != -1){ temp_words.splice(t+1, 0, " "); temp_words.splice(t+1, 0, " "); temp_words.splice(t+1, 0, " "); t++; t++; t++; } t++; } all_words = temp_words.slice(0); var currentWord = 0; var running = true; var spritz_timers = new Array(); document.getElementById("spritz_toggle").addEventListener("click", function() { if(running) { stopSpritz(); } else { startSpritz(); } }); function updateValues(i) { var p = pivot(all_words[i]); document.getElementById("spritz_result").innerHTML = p; currentWord = i; } function startSpritz() { document.getElementById("spritz_toggle").style.display = "block"; document.getElementById("spritz_toggle").textContent = "Pause"; running = true; spritz_timers.push(setInterval(function() { updateValues(currentWord); currentWord++; if(currentWord >= all_words.length) { currentWord = 0; stopSpritz(); } }, ms_per_word)); } function stopSpritz() { for(var i = 0; i < spritz_timers.length; i++) { clearTimeout(spritz_timers[i]); } document.getElementById("spritz_toggle").textContent = "Play"; running = false; } startSpritz(); } // Find the red-character of the current word. function pivot(word){ var length = word.length; var bestLetter = 1; switch (length) { case 1: bestLetter = 1; // first break; case 2: case 3: case 4: case 5: bestLetter = 2; // second break; case 6: case 7: case 8: case 9: bestLetter = 3; // third break; case 10: case 11: case 12: case 13: bestLetter = 4; // fourth break; default: bestLetter = 5; // fifth }; word = decodeEntities(word); var start = '.'.repeat((11-bestLetter)) + word.slice(0, bestLetter-1).replace('.', '•'); var middle = word.slice(bestLetter-1,bestLetter).replace('.', '•'); var end = word.slice(bestLetter, length).replace('.', '•') + '.'.repeat((11-(word.length-bestLetter))); var result; result = "" + start; result = result + ""; result = result + middle; result = result + ""; result = result + end; result = result + ""; result = result.replace(/\./g, ""); return result; } // Get the currently selected text, if any. // Shameless pinched from StackOverflow. function getSelectionText() { var text = ""; if (typeof window.getSelection != "undefined") { var sel = window.getSelection(); if (sel.rangeCount) { var container = document.createElement("div"); for (var i = 0, len = sel.rangeCount; i < len; ++i) { container.appendChild(sel.getRangeAt(i).cloneContents()); } text = container.innerText || container.textContent; } } else if (typeof document.selection != "undefined") { if (document.selection.type == "Text") { text = document.selection.createRange().text; } } if(text === ""){ return false; } else{ return text; } } // Uses the Readability API to get the juicy content of the current page. function spritzifyURL(){ var url = document.URL; //getURL("https://www.readability.com/api/content/v1/parser?url="+ encodeURIComponent(url) +"&token=" + readability_token +"&callback=?", getURL("https://api.diffbot.com/v2/article?url="+ encodeURIComponent(url) +"&token=" + diffbot_token, // +"&callback=?", function(data) { data = JSON.parse(data); if(data.error){ document.getElementById("spritz_result").innerText = "Article extraction failed. Try selecting text instead."; return; } var title = ''; if(data.title !== ""){ title = data.title + ". "; } var author = ''; if(data.author !== undefined){ author = "By " + data.author + ". "; } var body = data.text; body = body.trim(); // Trim trailing and leading whitespace. body = body.replace(/\s+/g, ' '); // Shrink long whitespaces. var text_content = title + author + body; text_content = text_content.replace(/\./g, '. '); // Make sure punctuation is apprpriately spaced. text_content = text_content.replace(/\?/g, '? '); text_content = text_content.replace(/\!/g, '! '); spritzify(text_content); }); } ////// // Helpers ////// // This is a hack using the fact that browers sequentially id the timers. function clearTimeouts(){ var id = window.setTimeout(function() {}, 0); while (id--) { window.clearTimeout(id); } } // Let strings repeat themselves, // because JavaScript isn't as awesome as Python. String.prototype.repeat = function( num ){ if(num < 1){ return new Array( Math.abs(num) + 1 ).join( this ); } return new Array( num + 1 ).join( this ); }; function decodeEntities(s){ var str, temp= document.createElement('p'); temp.innerHTML= s; str= temp.textContent || temp.innerText; temp=null; return str; }