NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Google Docs - Word Count (With Options)
// @namespace https://zachhardesty.com
// @author Zach Hardesty <zachhardesty7@users.noreply.github.com> (https://github.com/zachhardesty7)
// @description adds a word counter with options to Google Docs
// @copyright 2019, Zach Hardesty (https://zachhardesty.com/)
// @license GPL-3.0-only; http://www.gnu.org/licenses/gpl-3.0.txt
// @version 0.2.1
// @homepageURL https://github.com/zachhardesty7/tamper-monkey-scripts-collection/raw/master/google-docs-word-count.user.js
// @homepageURL https://openuserjs.org/scripts/zachhardesty7/Google_Docs_-_Word_Count_(With_Options)
// @supportURL https://openuserjs.org/scripts/zachhardesty7/Google_Docs_-_Word_Count_(With_Options)/issues
// @updateURL https://openuserjs.org/meta/zachhardesty7/Google_Docs_-_Word_Count_(With_Options).meta.js
// @downloadURL https://openuserjs.org/install/zachhardesty7/Google_Docs_-_Word_Count_(With_Options).user.js
// @match https://docs.google.com/document/*
// @require https://raw.githubusercontent.com/JensPLarsen/ChromeExtension-GoogleDocsUtil/master/googleDocsUtil.js
// ==/UserScript==
/* global googleDocsUtil */
// heavy inspiration from:
// https://greasyfork.org/en/scripts/22057-google-docs-wordcount/code
// https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep
// strikingly complex (uses DOM bounding boxes) to get currently selected text:
// may implement only necessary functions to save space, library size: (15.4 KB)
// https://github.com/JensPLarsen/ChromeExtension-GoogleDocsUtil
;(function displayCount() {
// words not counted between these when true
const BRACKETS = true
const PARENTHESIS = true
const QUOTES = true
const MISC = true // skips works cited, personal titles
const SELECTED = true // if selected text present, word count only counts it
const display = document.createElement("div")
display.id = "zh-display"
display.setAttribute(
"style",
`
position: fixed;
width: 100%;
left: 0px;
bottom: 0px;
color: rgba(0,0,0,.7);
height: 15px;
background-color: #ededee;
z-index: 100;
font-family: Arial;
font-size: 12px;
padding-top: 5px;
padding-left: 5px;
border-top: 1px solid #d9d9d9;
`
)
document.querySelector("body").append(display)
/**
* update the word count
*/
async function setCount() {
const doc = googleDocsUtil.getGoogleDocument()
let selected = doc.selectedText
const pages = document.querySelector(".kix-paginateddocumentplugin")
.children[1].children
let body = ""
;[...pages].forEach((page) => {
const content = page.lastElementChild.firstElementChild
const text = content.textContent
// pages that are unloaded will appear to have no text
// add a marker to the cumulative body to indicate that
// a word count should not be displayed
if (text === "") body += " ~~ "
body += text
})
// clean extra spaces
body = body.replace(/\u00A0/g, " ").trim()
// generate regex from settings
// must escape \'s in JS
// in standard regex form:
// /(“(.(?!“))+”)|(\((.(?!\())+\)|\[(.(?!\[))+\])
// |Works Cited(\n.*)*|(Unit \d (Primary Source Analysis|Exam: Part \d - #\d+))/g
const regex = []
if (BRACKETS) regex.push("\\[(.(?!\\[))+\\]")
if (PARENTHESIS) regex.push("\\((.(?!\\())+\\)")
if (QUOTES)
regex.push(
"Works Cited(.|\\n.*)*|(Unit \\d (Primary Source Analysis|Exam: Part \\d( - #\\d+)*))"
)
if (MISC) regex.push("(“(.(?!“))+”)")
// apply regex filtering to body
regex.forEach((reg) => {
selected = selected.replace(new RegExp(reg, "g"), " ")
})
// apply regex filtering to selected text if necessary
let filtered = body
regex.forEach((reg) => {
filtered = filtered.replace(new RegExp(reg, "g"), " ")
})
// remove extra spaces and line breaks and get counts
const words = filtered
.trim()
.replace(/\u00A0/g, " ")
.replace(/ {2,}/g, " ")
.split(" ")
if (words.includes("~~")) {
// empty or unloaded pages present
document.querySelector(
"#zh-display"
).textContent = `Word Count: (scroll to bottom & remove empty pages) | Pages: ${pages.length}`
} else if (selected.length > 0 && SELECTED) {
selected = selected
.trim()
.replace(/\u00A0/g, " ")
.replace(/ {2,}/g, " ")
.split(" ")
document.querySelector(
"#zh-display"
).textContent = `Word Count: ${selected.length} of ${words.length} (selected) | Pages: ${pages.length}`
} else {
document.querySelector(
"#zh-display"
).textContent = `Word Count: ${words.length} | Pages: ${pages.length}`
}
}
setInterval(setCount, 1000)
})()