UNPKG

eleventy-plugin-time-to-read

Version:

11ty plugin for estimating the time to read a given text. Supports multiple languages

188 lines (163 loc) 4.89 kB
const regEx = require('./regular-expressions.js'); module.exports = function(content, options) { const text = convertToPlainText(content); const parsedSpeed = parseSpeedOption(options.speed); const counts = getCounts(text); const totalSeconds = getTotalSeconds(counts, parsedSpeed); const timings = getTimings(totalSeconds, options.hours, options.minutes, options.seconds); let sentence = getTimeToRead(timings, options.language, options.style, options.type, options.digits); // Deprecated, remove in 2.0 major release if(/^only$/i.test(options.seconds)) { return totalSeconds; } else { if(options.prepend !== null) { sentence = options.prepend + sentence; } if(options.append !== null) { sentence = sentence + options.append; }; } return options.output({ timing: sentence, hours: timings.hours, minutes: timings.minutes, seconds: timings.seconds, totalCharacters: counts.characters, totalWords: counts.words, count: parsedSpeed.measure === 'word' ? counts.words : counts.characters, // Deprecated, remove in 2.0 major release totalSeconds: totalSeconds, speed: parsedSpeed, language: options.language }); } function convertToPlainText(content) { const html = content.templateContent || content; if(typeof html !== 'string') { throw new Error("Time-to-read's input must be a string or template"); } // Remove html return html.replace(new RegExp(regEx.html,'gi'), ''); } function parseSpeedOption(speedOption) { const speedOptions = speedOption.split(' '); function trimAndLowerCase(text) { if(text.endsWith('s')) { text = text.slice(0, -1); } return text.toLowerCase(); }; return { amount: Number(speedOptions[0]), measure: trimAndLowerCase(speedOptions[1]), interval: trimAndLowerCase(speedOptions[speedOptions.length - 1]) }; } function getCounts(text) { const counts = { characters: 0, words: 0 }; let currentWord = ''; for(const character of text) { // If the character is whitespace if(/\s/.test(character)) { if(currentWord === '') { continue; } else { counts.characters += currentWord.normalize('NFC').length; counts.words++; currentWord = ''; } } else { currentWord += character; } } if(currentWord !== '') { counts.characters += currentWord.normalize('NFC').length; counts.words++; } return counts; } function getTotalSeconds(count, speed) { if(speed.measure === 'word') { count = count.words; } else if(speed.measure === 'character') { count = count.characters; } // Normalise to seconds switch(speed.interval) { case('hour'): count *= 60; case('minute'): count *= 60; } return Math.ceil(count / speed.amount); } function getTimings(totalSeconds, showHours, showMinutes, showSeconds) { let hours, minutes, seconds; if(showHours && showMinutes && !showSeconds) { hours = Math.floor(totalSeconds / 3600); minutes = Math.round((totalSeconds % 3600) / 60); if(hours === 0 && minutes === 0) { minutes = 1; } } else if(showHours && showMinutes && showSeconds) { hours = Math.floor(totalSeconds / 3600); minutes = Math.floor((totalSeconds % 3600) / 60); seconds = totalSeconds % 60; } else if(!showHours && showMinutes && !showSeconds) { minutes = Math.max(1, Math.round(totalSeconds / 60)); } else if(!showHours && showMinutes && showSeconds) { minutes = Math.floor(totalSeconds / 60); seconds = totalSeconds % 60; } else if(!showHours && !showMinutes && showSeconds) { seconds = Math.max(1, totalSeconds); } else if(showHours && !showMinutes && showSeconds) { hours = Math.floor(totalSeconds / 3600); seconds = totalSeconds % 3600; } else if(showHours && !showMinutes && !showSeconds) { hours = Math.max(1, Math.round(totalSeconds / 3600)); } if(!showHours || (/^auto$/i.test(showHours) && hours === 0)) { hours = null; } if(!showMinutes || (/^auto$/i.test(showMinutes) && minutes === 0)) { minutes = null; } if(!showSeconds || (/^auto$/i.test(showSeconds) && seconds === 0)) { seconds = null; } return { hours, minutes, seconds }; } function getTimeToRead(timings, language, style, type, digits) { function addLabel(unit, amount) { return new Intl.NumberFormat(language, { style: 'unit', unit: unit, unitDisplay: style, minimumIntegerDigits: digits }).format(amount); } let timeUnits = []; if(timings.hours !== null) { timeUnits.push(addLabel('hour', timings.hours)); } if(timings.minutes !== null) { timeUnits.push(addLabel('minute', timings.minutes)); } if(timings.seconds !== null) { timeUnits.push(addLabel('second', timings.seconds)); } return new Intl.ListFormat(language, { type: type, style: style }).format(timeUnits); }