|
$("document").ready(function(){ |
|
|
|
var SpritzItClass = function(){ |
|
var spritzIt = {}; |
|
|
|
|
|
|
|
|
|
|
|
spritzIt.spritzIt = function(){ |
|
//Get text as an array |
|
var words = $("#src").val().split(" "); |
|
var processedlist = []; |
|
//Clear earlier output, not used anymore |
|
this.clearOut(); |
|
//Analyze each word to get their ORP - optimal recognition point |
|
for( var word in words ){ |
|
processedlist.push( this.analyze(words[word]) ); |
|
} |
|
//Show the text to the user in a timely fashion! |
|
this.render(processedlist, parseInt( $("#wpm").val() ) ); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
spritzIt.clearOut = function(){ |
|
$("#target").html(""); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
spritzIt.out = function(a1, a2, red){ |
|
if (red === undefined){ |
|
red = null; |
|
} |
|
|
|
var indexOfWeightedMedian = this.findMiddle(a2.slice()); |
|
|
|
var html = ""; |
|
for (var i = 0; i < a1.length; i++){ |
|
html += "<span"; |
|
if( i == indexOfWeightedMedian){ |
|
html += " style='color:red'"; |
|
} |
|
html += ">" + a1[i] + "</span>"; |
|
} |
|
html += " "; |
|
$("#target").append(html); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
spritzIt.analyze = function(val){ |
|
var obj = {}; |
|
|
|
var a1 = val.split(""); |
|
var a2 = []; |
|
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVXYZÅÄÖabcdefghijklmnopqrstuvxyzåäö 0123456789".split(""); |
|
//console.log(alphabet); |
|
for (var i = 0; i < a1.length; i++){ |
|
//a2.push(alphabet.indexOf(a1[i]) + 1); |
|
if(alphabet.indexOf(a1[i]) >= 0){ |
|
a2.push(this.measurements[alphabet.indexOf(a1[i])]); |
|
}else{ |
|
a2.push(this.getMeasurementFor(a1[i])); |
|
} |
|
} |
|
|
|
obj.word = val; |
|
obj.wordArr = a1.slice(); |
|
obj.measurements = a2.slice(); |
|
obj.middle = this.findMiddle(a2.slice()); |
|
|
|
this. out(a1, a2); |
|
return obj; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
spritzIt.measureLetters = function(){ |
|
var alphabet = "ABCDEFGHIJKLMNOPQRSTUVXYZÅÄÖabcdefghijklmnopqrstuvxyzåäö 0123456789".split(""); |
|
var measurements = []; |
|
for (var i = 0; i < alphabet.length; i++){ |
|
$("#letters").append( |
|
"<div id=\"alpha-" + alphabet[i].replace(" ", "nbsp") + "\">" + |
|
alphabet[i].replace(" ", " ") + |
|
"</div>"); |
|
measurements.push( $("#alpha-" + alphabet[i].replace(" ", "nbsp")).width() ); |
|
} |
|
return measurements; |
|
|
|
|
|
|
|
|
|
|
|
}; |
|
spritzIt.getMeasurementFor = function(char){ |
|
var id = Math.round(Math.random()*100000); |
|
$("#letters").append( |
|
"<div id=\"alpha-" + id + "\">" + |
|
char.replace(" ", " ") + |
|
"</div>"); |
|
return $("#alpha-" + id).width(); |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
spritzIt.findMiddle = function(arr){ |
|
|
|
/* |
|
After analyzing a word, we get an array like this: |
|
|
|
N, a,t,i,o,n,a,l,e,n <- letter |
|
12,7,4,4,8,8,7,4,7,8 <- character width in px |
|
|
|
Starting with 0, we're gonna add from the left until it's >0, |
|
and then we're gonna subtract from the right until it's <0. |
|
Like this: |
|
|
|
N, a,t,i,o,n,a,l,e,n |
|
12,7,4,4,8,8,7,4,7,8 |
|
+12 Result: 12 |
|
|
|
When we've added the first "12", we remove it from the array. |
|
Our result is now 12, so we'll subtract the number to the far right; 8: |
|
|
|
,7,4,4,8,8,7,4,7,8 |
|
-8 Result: 4 |
|
|
|
And so it continues until the balanced middle is found. |
|
|
|
,7,4,4,8,8,7,4,7, |
|
-7 Result: -3 |
|
|
|
,7,4,4,8,8,7,4,, |
|
+7 Result: 4 |
|
|
|
,,4,4,8,8,7,4,, |
|
-4 Result: 0 |
|
|
|
,,4,4,8,8,7,,, |
|
+4 Result: 4 |
|
|
|
,,,4,8,8,7,,, <-- due to a bug, my calculations stopped at this step. At first I just fixed it, |
|
-7 Result: -34 but then I changed it back since it made it look more like the Spritz original. Also, it felt better to read. |
|
|
|
,,,4,8,8,,,, |
|
+4 Result: 1 |
|
|
|
,,,,8,8,,,, |
|
-8 Result: -7 |
|
|
|
,,,,8,,,,, |
|
+8 Result: 1 |
|
|
|
Verifying that this i actually the center, meaning the sum of each of the two sides are as close as they can be: |
|
12,7,4,4,*8*,8,7,4,7,8 |
|
12+7+4+4 = 27 |
|
8+7+4+7+8 = 34 |
|
diff 7 |
|
|
|
Comparing to the result if center is shifted one step to the left... |
|
12+7+4 = 23 |
|
8+8+7+4+7+8 = 42 |
|
diff 19 |
|
|
|
...and to the right, the diff is minimal in the calculated position. |
|
12+7+4+4+8 = 45 |
|
7+4+7+8 = 26 |
|
diff 19 |
|
*/ |
|
|
|
var res = 0; |
|
var pos = 0; |
|
|
|
if (arr.length == 1) { |
|
return 0; |
|
} |
|
|
|
//The bug referred to in the explanation above, is that arr.length is updated after each splice. |
|
//Thus, as "i" is growing bigger, the limit is shrinking, making the loop run only half as many times as first intended. |
|
for ( var i = 0; i < arr.length; i++ ){ |
|
if(res <= 0){ |
|
//If result <= 0, add from the left |
|
res += parseInt(arr[0]); |
|
arr.splice(0,1); |
|
pos += 1; |
|
}else{ |
|
//If result > 0, subtract from the right |
|
res -= parseInt(arr[arr.length-1]); |
|
arr.splice(arr.length-1,1); |
|
} |
|
} |
|
//Return position of the "optimal reading point". |
|
return pos; |
|
}; |
|
|
|
|
|
|
|
|
|
//Recursive function, showing the next word in the array |
|
spritzIt.nextWord = function(obj, index, wordsPerMinute, pause){ |
|
|
|
/* |
|
Positioning the ORP |
|
*/ |
|
//calculate offset |
|
var margin = 100; |
|
for (var i = 0; i <= obj[index].middle; i++){ |
|
margin -= obj[index].measurements[i]; |
|
|
|
//center the ORP letter |
|
if( i == obj[index].middle){ |
|
margin += parseInt(obj[index].measurements[i])/2; |
|
} |
|
} |
|
|
|
//Print word |
|
var word = ""; |
|
for (var char in obj[index].wordArr){ |
|
if(char == obj[index].middle){ |
|
word += "<span class=\"red\">" + obj[index].wordArr[char] + "</span>"; |
|
}else{ |
|
word += obj[index].wordArr[char]; |
|
} |
|
} |
|
|
|
//If pausing (blanking the window after a punctuation), clear the output |
|
if(pause){ |
|
$("#player").html(""); |
|
}else{ |
|
$("#player").html("<div style='margin-left:"+margin+"px;'>" + word + "</div>" ); |
|
} |
|
|
|
//set timeout for next word |
|
//check word for pausing characters (:;-,.) |
|
var longPauseChars = ".;"; |
|
var shortPauseChars = ",:-"; |
|
var timeout = Math.round(60000 / wordsPerMinute); |
|
var timeoutUnit = Math.round(60000 / wordsPerMinute); |
|
|
|
//Slow down on the first two words |
|
if(index === 0){ |
|
timeout = timeout * 3; |
|
} |
|
if(index == 1){ |
|
timeout = timeout * 2; |
|
} |
|
|
|
//Idea: set a word-length-multiplier about here. |
|
//Hard to find good settings, though. |
|
//timeout = timeout * 0.5 + (timeout / 3 * (obj[index].word.length + 2) / 4); |
|
|
|
/* |
|
Punctuations |
|
*/ |
|
//In spritz, a punctuation is followed by an "empty" word. Let's do that. |
|
var makeAPause = false; |
|
|
|
//long punctuation |
|
if( obj[index].word.match(/.*[.;!?].*/g) ){ |
|
timeout = timeout + timeoutUnit * 1.5; |
|
//In spritz, a punctuation is followed by an "empty" word. Let's do that. |
|
makeAPause = true; |
|
} |
|
//short punctuation |
|
if( obj[index].word.match(/.*[\(\),:-].*/g) ){ |
|
timeout = timeout + timeoutUnit * 1; |
|
} |
|
//Did we just make a pause? Don't pause again, and back the index up a notch. Also, make sure to take that pause. |
|
if(pause){ |
|
makeAPause = false; |
|
index = index - 1; |
|
timeout = timeout + timeoutUnit * 2; |
|
} |
|
|
|
totalTime += timeout; |
|
|
|
//Show next word |
|
var that = this; |
|
if(index < obj.length-1){ |
|
//Regular loop |
|
setTimeout(function(){ |
|
that.nextWord(obj, index + 1, wordsPerMinute, makeAPause); |
|
}, timeout); |
|
}else{ |
|
//Last word, clear out |
|
setTimeout(function(){ |
|
//Empty player |
|
$("#player").html(""); |
|
console.log("Total time: " + totalTime + "ms for " + obj.length + "words. That's " + 60000 / (totalTime / obj.length) + "wpm."); |
|
}, timeout * 2); |
|
} |
|
}; |
|
|
|
|
|
|
|
|
|
//Starts the word animation |
|
spritzIt.render = function(obj, wordsPerMinute){ |
|
if(wordsPerMinute === undefined){ |
|
wordsPerMinute = 300; |
|
} |
|
this.nextWord(obj, 0, wordsPerMinute); |
|
}; |
|
|
|
var totalTime = 0; |
|
return spritzIt; |
|
}; |
|
|
|
var mySpritz = new SpritzItClass(); |
|
|
|
//Button event handler |
|
$("#goBtn").click(function(){ |
|
mySpritz.spritzIt(); |
|
}); |
|
|
|
//Analyze the font used, by putting letters in divs and measuring them |
|
mySpritz.measurements = mySpritz.measureLetters(); |
|
|
|
//Start player on page load |
|
mySpritz.spritzIt(); |
|
|
|
}); |