Action
Stats (with Read Times)
Calculate and present preview of common readability statistics for the current draft.
Slight modification to original Stats Action to include Read Aloud and Silent Read times for the current draft.
Steps
-
script
// Make sure draft is not blank first... if (draft.content.length == 0) { alert("Empty draft. Text required to calculate statistics."); context.cancel(); }
-
script
// Readability Stats with Read Times // This is the middle step. It comes after a blank-draft check and before a CSS table formatting step // Show alert displaying common text statistics for current draft // Includes syllable counts, Flesh-Kincaid, SMOG index scoring, etc. // adapted from... // TextStatistics.js // Christopher Giffard (2012) // 1:1 API Fork of TextStatistics.php by Dave Child (Thanks mate!) // https://github.com/DaveChild/Text-Statistics // Read Time functions added by Gil Rognstad // BEGIN LIBRARY function cleanText(text) { // all these tags should be preceeded by a full stop. var fullStopTags = ['li', 'p', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'dd']; fullStopTags.forEach(function(tag) { text = text.replace("</" + tag + ">","."); }) text = text .replace(/<[^>]+>/g, "") // Strip tags .replace(/[,:;()\-]/, " ") // Replace commans, hyphens etc (count them as spaces) .replace(/[\.!?]/, ".") // Unify terminators .replace(/^\s+/,"") // Strip leading whitespace .replace(/[ ]*(\n|\r\n|\r)[ ]*/," ") // Replace new lines with spaces .replace(/([\.])[\. ]+/,".") // Check for duplicated terminators .replace(/[ ]*([\.])/,". ") // Pad sentence terminators .replace(/\s+/," ") // Remove multiple spaces .replace(/\s+$/,""); // Strip trailing whitespace text += "."; // Add final terminator, just in case it's missing. return text; } var TextStatistics = function TextStatistics(text) { this.text = text ? cleanText(text) : this.text; }; TextStatistics.prototype.fleschKincaidReadingEase = function(text) { text = text ? cleanText(text) : this.text; return Math.round((206.835 - (1.015 * this.averageWordsPerSentence(text)) - (84.6 * this.averageSyllablesPerWord(text)))*10)/10; }; TextStatistics.prototype.fleschKincaidGradeLevel = function(text) { text = text ? cleanText(text) : this.text; return Math.round(((0.39 * this.averageWordsPerSentence(text)) + (11.8 * this.averageSyllablesPerWord(text)) - 15.59)*10)/10; }; TextStatistics.prototype.gunningFogScore = function(text) { text = text ? cleanText(text) : this.text; return Math.round(((this.averageWordsPerSentence(text) + this.percentageWordsWithThreeSyllables(text, false)) * 0.4)*10)/10; }; TextStatistics.prototype.colemanLiauIndex = function(text) { text = text ? cleanText(text) : this.text; return Math.round(((5.89 * (this.letterCount(text) / this.wordCount(text))) - (0.3 * (this.sentenceCount(text) / this.wordCount(text))) - 15.8 ) *10)/10; }; TextStatistics.prototype.smogIndex = function(text) { text = text ? cleanText(text) : this.text; return Math.round(1.043 * Math.sqrt((this.wordsWithThreeSyllables(text) * (30 / this.sentenceCount(text))) + 3.1291)*10)/10; }; TextStatistics.prototype.automatedReadabilityIndex = function(text) { text = text ? cleanText(text) : this.text; return Math.round(((4.71 * (this.letterCount(text) / this.wordCount(text))) + (0.5 * (this.wordCount(text) / this.sentenceCount(text))) - 21.43)*10)/10; }; TextStatistics.prototype.textLength = function(text) { text = text ? cleanText(text) : this.text; return text.length; }; TextStatistics.prototype.letterCount = function(text) { text = text ? cleanText(text) : this.text; text = text.replace(/[^a-z]+/ig,""); return text.length; }; TextStatistics.prototype.sentenceCount = function(text) { text = text ? cleanText(text) : this.text; // Will be tripped up by "Mr." or "U.K.". Not a major concern at this point. return text.replace(/[^\.!?]/g, '').length || 1; }; TextStatistics.prototype.wordCount = function(text) { text = text ? cleanText(text) : this.text; return text.split(/[^a-z0-9]+/i).length || 1; }; TextStatistics.prototype.averageWordsPerSentence = function(text) { text = text ? cleanText(text) : this.text; return this.wordCount(text) / this.sentenceCount(text); }; TextStatistics.prototype.readAloudTimeSlow = function(text) { text = text ? cleanText(text) : this.text; return Math.round(this.wordCount(text) / 100); }; TextStatistics.prototype.readAloudTimeRelaxed = function(text) { text = text ? cleanText(text) : this.text; return Math.round(this.wordCount(text) / 130); }; TextStatistics.prototype.readAloudTimeFast = function(text) { text = text ? cleanText(text) : this.text; return Math.round(this.wordCount(text) / 160); }; TextStatistics.prototype.silentReadTime = function(text) { text = text ? cleanText(text) : this.text; return Math.round(this.wordCount(text) / 200); }; TextStatistics.prototype.averageSyllablesPerWord = function(text) { text = text ? cleanText(text) : this.text; var syllableCount = 0, wordCount = this.wordCount(text), self = this; text.split(/\s+/).forEach(function(word) { syllableCount += self.syllableCount(word); }); // Prevent NaN... return (syllableCount||1) / (wordCount||1); }; TextStatistics.prototype.wordsWithThreeSyllables = function(text, countProperNouns) { text = text ? cleanText(text) : this.text; var longWordCount = 0, self = this; countProperNouns = countProperNouns === false ? false : true; text.split(/\s+/).forEach(function(word) { // We don't count proper nouns or capitalised words if the countProperNouns attribute is set. // Defaults to true. if (!word.match(/^[A-Z]/) || countProperNouns) { if (self.syllableCount(word) > 2) longWordCount ++; } }); return longWordCount; }; TextStatistics.prototype.percentageWordsWithThreeSyllables = function(text, countProperNouns) { text = text ? cleanText(text) : this.text; return (this.wordsWithThreeSyllables(text,countProperNouns) / this.wordCount(text)) * 100; }; TextStatistics.prototype.syllableCount = function(word) { var syllableCount = 0, prefixSuffixCount = 0, wordPartCount = 0; // Prepare word - make lower case and remove non-word characters word = word.toLowerCase().replace(/[^a-z]/g,""); // Specific common exceptions that don't follow the rule set below are handled individually // Array of problem words (with word as key, syllable count as value) var problemWords = { "simile": 3, "forever": 3, "shoreline": 2 }; // Return if we've hit one of those... if (problemWords.hasOwnProperty(word)) return problemWords[word]; // These syllables would be counted as two but should be one var subSyllables = [ /cial/, /tia/, /cius/, /cious/, /giu/, /ion/, /iou/, /sia$/, /[^aeiuoyt]{2,}ed$/, /.ely$/, /[cg]h?e[rsd]?$/, /rved?$/, /[aeiouy][dt]es?$/, /[aeiouy][^aeiouydt]e[rsd]?$/, /^[dr]e[aeiou][^aeiou]+$/, // Sorts out deal, deign etc /[aeiouy]rse$/ // Purse, hearse ]; // These syllables would be counted as one but should be two var addSyllables = [ /ia/, /riet/, /dien/, /iu/, /io/, /ii/, /[aeiouym]bl$/, /[aeiou]{3}/, /^mc/, /ism$/, /([^aeiouy])\1l$/, /[^l]lien/, /^coa[dglx]./, /[^gq]ua[^auieo]/, /dnt$/, /uity$/, /ie(r|st)$/ ]; // Single syllable prefixes and suffixes var prefixSuffix = [ /^un/, /^fore/, /ly$/, /less$/, /ful$/, /ers?$/, /ings?$/ ]; // Remove prefixes and suffixes and count how many were taken prefixSuffix.forEach(function(regex) { if (word.match(regex)) { word = word.replace(regex,""); prefixSuffixCount ++; } }); wordPartCount = word .split(/[^aeiouy]+/ig) .filter(function(wordPart) { return !!wordPart.replace(/\s+/ig,"").length }) .length; // Get preliminary syllable count... syllableCount = wordPartCount + prefixSuffixCount; // Some syllables do not follow normal rules - check for them subSyllables.forEach(function(syllable) { if (word.match(syllable)) syllableCount --; }); addSyllables.forEach(function(syllable) { if (word.match(syllable)) syllableCount ++; }); return syllableCount || 1; }; function textStatistics(text) { return new TextStatistics(text); } // END LIBRARY var result = ""; var s = draft.content; var stats = textStatistics(s); var t = ["<table>"]; function addRow(key, val, link) { t.push("<tr><td class='lbl'>"); t.push(key); t.push("</td><td class='val'>"); t.push(val); t.push("</td>"); if (link) { if (link == "mins") { t.push("<td>"); t.push("mins"); t.push("</td>"); } else { t.push("<td>"); t.push("<a href='" + link + "'>info</a>"); t.push("</td>"); }} else { t.push("<td></td>"); } t.push("</tr>") } addRow("Words", stats.wordCount()); addRow("Sentences", stats.sentenceCount()); addRow("Words per sentence", Math.round(stats.averageWordsPerSentence(), 2)); addRow("Flesch-Kincaid Ease", stats.fleschKincaidReadingEase(), "http://www.readabilityformulas.com/flesch-reading-ease-readability-formula.php"); addRow("Flesch-Kincaid Grade Level", stats.fleschKincaidGradeLevel(), "http://www.readabilityformulas.com/flesch-grade-level-readability-formula.php"); addRow("Gunning Fog", stats.gunningFogScore(), "https://en.wikipedia.org/wiki/Gunning_fog_index"); addRow("SMOG Index", stats.smogIndex(), "http://www.readabilityformulas.com/smog-readability-formula.php"); addRow("Automated Readability", stats.automatedReadabilityIndex(), "http://www.readabilityformulas.com/automated-readability-index.php"); addRow("Coleman Liau", stats.colemanLiauIndex(), "http://www.readabilityformulas.com/coleman-liau-readability-formula.php"); addRow("Read Aloud Time (Slow)", stats.readAloudTimeSlow(), "mins"); addRow("Read Aloud Time (Relaxed)", stats.readAloudTimeRelaxed(), "mins"); addRow("Read Aloud Time (Fast)", stats.readAloudTimeFast(), "mins"); addRow("Silent Read Time", stats.silentReadTime(), "mins"); t.push("</table>"); draft.setTemplateTag("htmltable", t.join(""));
-
script
// create theme sensitive css for preview if (app.themeMode == 'dark') { var css = "html, body { background: #222; color: #ddd; }"; draft.setTemplateTag("bodystyle", css); } else { var css = "html, body { background: #fff; color: #444; }"; draft.setTemplateTag("bodystyle", css); }
-
htmlpreview
<!DOCTYPE html> <html dir="auto"> <head> <title>Preview</title> <meta name="viewport" content="width=device-width, initial-scale=1"> <style> @import url(http://fonts.googleapis.com/css?family=Vollkorn:400,400italic,700,700italic&subset=latin); html, body { padding:0em; margin:auto; max-width:55em; text-align: center; } body { font: 1.3em "Vollkorn", Palatino, Times; color: #333; line-height: 1.4; text-align: center; } header, nav, article, footer { width: 700px; margin:0 auto; } article { margin-top: 4em; margin-bottom: 4em; min-height: 400px; } footer { margin-bottom:50px; } video { margin: 2em 0; border:1px solid #ddd; } nav { font-size: .9em; font-style: italic; border-bottom: 1px solid #ddd; padding: 1em 0; } nav p { margin: 0; } /* Typography -------------------------------------------------------- */ h1 { margin-top: 0; font-weight: normal; } h2 { font-weight: normal; } h3 { font-weight: normal; font-style: italic; margin-top:3em; } p { margin-top:0; -webkit-hypens:auto; -moz-hypens:auto; hyphens:auto; } ul { list-style: square; padding-left:1.2em; } ol { padding-left:1.2em; } blockquote { margin-left: 1em; padding-left: 1em; border-left: 1px solid #ddd; } code { font-family: "Consolas", "Menlo", "Monaco", monospace, serif; font-size: .9em; background: white; } a { color: #2484c1; text-decoration: none; } a:hover { text-decoration: underline; } a img { border:none; } h1 a, h1 a:hover { color: #333; text-decoration: none; } hr { color : #ddd; height : 1px; margin: 2em 0; border-top : solid 1px #ddd; border-bottom : none; border-left: 0; border-right: 0; } p#heart{ font-size: 2em; line-height: 1; text-align: center; color: #ccc; } .red { color:#B50000; } img { max-width: 90%; } table { margin: 1em auto; border: 1px solid #aaa; border-collapse: collapse; } th { padding:.25em .5em; background: #efefef; border: 1px solid #ccc; } td { padding:.25em .5em; text-align: left; border: 1px solid #ccc; } /* Home Page --------------------------- */ body#index li { margin-bottom: 1em; } /* iPad -------------------------------------------------------- */ @media only screen and (max-device-width: 1024px) { body { font-size: 120%; line-height: 1.4; } } /* @iPad */ /* iPhone -------------------------------------------------------- */ @media only screen and (max-device-width: 480px) { body { text-align: left; padding:.5em; } article, footer { width: auto; } article { padding: 0 10px; } } /* @iPhone */ .lbl { } .val { font-weight: bold; text-align: right; } [[bodystyle]] </style> </head> <body> <h2>Draft Statistics</h2> [[htmltable]] </body> </html>
Options
-
After Success Nothing Notification Error Log Level Error
Items available in the Drafts Directory are uploaded by community members. Use appropriate caution reviewing downloaded items before use.