/* THIS IS A GENERATED/BUNDLED FILE BY ESBUILD if you want to view the source, please visit the github repository of this plugin */ var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); // main.ts var main_exports = {}; __export(main_exports, { default: () => NovelWordCountPlugin }); module.exports = __toCommonJS(main_exports); // logic/debug.ts var DebugHelper = class { constructor() { this.debugMode = false; this.idCounter = 0; } setDebugMode(debug) { this.debugMode = debug; } debug(...args) { if (!this.debugMode) { return; } console.log("novel-word-count:", ...args); } error(message) { if (!this.debugMode) { return; } console.error(message); } debugStart(name) { if (!this.debugMode) { return () => { }; } var qualifiedName = `novel-word-count|${name} (${++this.idCounter})`; console.time(qualifiedName); return () => console.timeEnd(qualifiedName); } }; // logic/event.ts var import_obsidian = require("obsidian"); // logic/cancellation.ts var CANCEL = Symbol("Cancel"); var CancellationToken = class { constructor() { this._isCancelled = false; } get isCancelled() { return this._isCancelled; } [CANCEL]() { this._isCancelled = true; } }; var CancellationTokenSource = class { constructor() { this.token = new CancellationToken(); } cancel() { this.token[CANCEL](); } }; // logic/event.ts var EventHelper = class { constructor(plugin, app, debugHelper, fileHelper) { this.plugin = plugin; this.app = app; this.debugHelper = debugHelper; this.fileHelper = fileHelper; this.cancellationSources = []; } async handleEvents() { const debouncedFileModified = (0, import_obsidian.debounce)(async (file) => { const countToken = this.registerNewCountToken(); await this.fileHelper.updateFileCounts( file, this.plugin.savedData.cachedCounts, countToken.token ); this.cancelToken(countToken); await this.plugin.updateDisplayedCounts(file); this.plugin.saveSettingsDebounced(); }, 500); this.plugin.registerEvent( this.app.metadataCache.on("changed", async (file) => { this.debugHelper.debug( "[changed] metadataCache hook fired, scheduling file for analysis", file.path ); debouncedFileModified(file); }) ); this.app.workspace.onLayoutReady(() => { this.plugin.registerEvent( this.app.vault.on("create", async (file) => { this.debugHelper.debug( "[create] vault hook fired, analyzing file", file.path ); const countToken = this.registerNewCountToken(); await this.fileHelper.updateFileCounts( file, this.plugin.savedData.cachedCounts, countToken.token ); this.cancelToken(countToken); await this.plugin.updateDisplayedCounts(file); await this.plugin.saveSettings(); }) ); this.plugin.registerEvent( this.app.vault.on("modify", async (file) => { this.debugHelper.debug( "[modify] vault hook fired, scheduling file for analysis", file.path ); debouncedFileModified(file); }) ); }); this.plugin.registerEvent( this.app.vault.on("delete", async (file) => { this.debugHelper.debug( "[delete] vault hook fired, forgetting file", file.path ); this.fileHelper.removeFileCounts( file.path, this.plugin.savedData.cachedCounts ); await this.plugin.updateDisplayedCounts(file); await this.plugin.saveSettings(); }) ); this.plugin.registerEvent( this.app.vault.on("rename", async (file, oldPath) => { if (file instanceof import_obsidian.TFolder) { return; } this.debugHelper.debug( "[rename] vault hook fired, recounting file", file.path ); this.fileHelper.removeFileCounts( oldPath, this.plugin.savedData.cachedCounts ); const countToken = this.registerNewCountToken(); await this.fileHelper.updateFileCounts( file, this.plugin.savedData.cachedCounts, countToken.token ); this.cancelToken(countToken); await this.plugin.updateDisplayedCounts(file); await this.plugin.saveSettings(); }) ); const reshowCountsIfNeeded = async (hookName) => { this.debugHelper.debug(`[${hookName}] hook fired`); const fileExplorerLeaf = await this.plugin.getFileExplorerLeaf(); if (this.isContainerTouched(fileExplorerLeaf)) { this.debugHelper.debug( "container already touched, skipping display update" ); return; } this.debugHelper.debug("container is clean, updating display"); await this.plugin.updateDisplayedCounts(); }; this.plugin.registerEvent( this.app.workspace.on( "layout-change", (0, import_obsidian.debounce)(reshowCountsIfNeeded.bind(this, "layout-change"), 1e3) ) ); } isContainerTouched(leaf) { const container = leaf.view.containerEl; return container.className.includes("novel-word-count--"); } async reinitializeAllCounts() { this.cancelAllCountTokens(); const countToken = this.registerNewCountToken(); this.debugHelper.debug("refreshAllCounts"); this.plugin.savedData.cachedCounts = await this.fileHelper.initializeAllFileCounts( countToken.token ); this.cancelToken(countToken); await this.plugin.saveSettings(); } /* CANCELLATION HANDLING */ registerNewCountToken() { const cancellationSource = new CancellationTokenSource(); this.cancellationSources.push(cancellationSource); return cancellationSource; } cancelToken(source) { source.cancel(); if (this.cancellationSources.includes(source)) { this.cancellationSources.splice( this.cancellationSources.indexOf(source), 1 ); } } cancelAllCountTokens() { for (const source of this.cancellationSources) { source.cancel(); } this.cancellationSources = []; } }; // logic/file.ts var import_obsidian2 = require("obsidian"); // logic/settings.ts var $CountType = /* @__PURE__ */ (($CountType2) => { $CountType2["None"] = "none"; $CountType2["Word"] = "word"; $CountType2["Page"] = "page"; $CountType2["PageDecimal"] = "pagedecimal"; $CountType2["Linebreak"] = "linebreak"; $CountType2["ReadTime"] = "readtime"; $CountType2["PercentGoal"] = "percentgoal"; $CountType2["Note"] = "note"; $CountType2["Character"] = "character"; $CountType2["Link"] = "link"; $CountType2["Embed"] = "embed"; $CountType2["Alias"] = "alias"; $CountType2["Created"] = "created"; $CountType2["Modified"] = "modified"; $CountType2["FileSize"] = "filesize"; $CountType2["FrontmatterKey"] = "frontmatterKey"; $CountType2["TrackSession"] = "tracksession"; return $CountType2; })($CountType || {}); var COUNT_TYPES = Object.values($CountType); var SESSION_COUNT_TYPES = [ "word" /* Word */, "page" /* Page */, "pagedecimal" /* PageDecimal */, "linebreak" /* Linebreak */, "note" /* Note */, "character" /* Character */ ]; var COUNT_TYPE_DISPLAY_STRINGS = { ["none" /* None */]: "None", ["word" /* Word */]: "Word Count", ["page" /* Page */]: "Page Count", ["pagedecimal" /* PageDecimal */]: "Page Count (decimal)", ["linebreak" /* Linebreak */]: "Line Break Count", ["readtime" /* ReadTime */]: "Reading Time", ["percentgoal" /* PercentGoal */]: "% of Word Goal", ["note" /* Note */]: "Note Count", ["character" /* Character */]: "Character Count", ["link" /* Link */]: "Link Count", ["embed" /* Embed */]: "Embed Count", ["alias" /* Alias */]: "First Alias", ["created" /* Created */]: "Created Date", ["modified" /* Modified */]: "Last Updated Date", ["filesize" /* FileSize */]: "File Size", ["frontmatterKey" /* FrontmatterKey */]: "Frontmatter Key", ["tracksession" /* TrackSession */]: "Track Session" }; var COUNT_TYPE_DESCRIPTIONS = { ["none" /* None */]: "Hidden.", ["word" /* Word */]: "Total words.", ["page" /* Page */]: "Total pages, rounded up.", ["pagedecimal" /* PageDecimal */]: "Total pages, precise to 2 digits after the decimal.", ["linebreak" /* Linebreak */]: "Newlines (\xB6), including empty lines.", ["readtime" /* ReadTime */]: "Estimated time to read the note.", ["percentgoal" /* PercentGoal */]: "Set a word goal by adding the 'word-goal' property to a note.", ["note" /* Note */]: "Total notes.", ["character" /* Character */]: "Total characters (letters, symbols, numbers, and spaces).", ["link" /* Link */]: "Total links to other notes.", ["embed" /* Embed */]: "Total embedded images, files, and notes.", ["alias" /* Alias */]: "The first alias property of each note.", ["created" /* Created */]: "Creation date. (On folders: earliest creation date of any note.)", ["modified" /* Modified */]: "Date of last edit. (On folders: latest edit date of any note.)", ["filesize" /* FileSize */]: "Total size on hard drive.", ["frontmatterKey" /* FrontmatterKey */]: "Key in the frontmatter block.", ["tracksession" /* TrackSession */]: "Track progress since last Obsidian startup, plugin init, settings change, or recount" }; var UNFORMATTABLE_COUNT_TYPES = [ "none" /* None */, "alias" /* Alias */, "filesize" /* FileSize */, "readtime" /* ReadTime */ ]; var COUNT_TYPE_DEFAULT_SHORT_SUFFIXES = { ["word" /* Word */]: "w", ["page" /* Page */]: "p", ["pagedecimal" /* PageDecimal */]: "p", ["linebreak" /* Linebreak */]: "\xB6", ["percentgoal" /* PercentGoal */]: "%", ["note" /* Note */]: "n", ["character" /* Character */]: "ch", ["link" /* Link */]: "x", ["embed" /* Embed */]: "em", ["created" /* Created */]: "/c", ["modified" /* Modified */]: "/u", ["frontmatterKey" /* FrontmatterKey */]: "", ["tracksession" /* TrackSession */]: "/s" }; function getDescription(countType) { return `[${COUNT_TYPE_DISPLAY_STRINGS[countType]}] ${COUNT_TYPE_DESCRIPTIONS[countType]}`; } var ALIGNMENT_TYPES = [ "inline" /* Inline */, "right" /* Right */, "below" /* Below */ ]; var DEFAULT_SETTINGS = { // FORMATTING useAdvancedFormatting: false, // NOTES countType: "word" /* Word */, countConfig: { customSuffix: "w", $sessionCountType: "word" /* Word */ }, countType2: "none" /* None */, countConfig2: { $sessionCountType: "word" /* Word */ }, countType3: "none" /* None */, countConfig3: { $sessionCountType: "word" /* Word */ }, pipeSeparator: "|", abbreviateDescriptions: false, alignment: "inline" /* Inline */, // FOLDERS showSameCountsOnFolders: true, folderCountType: "word" /* Word */, folderCountConfig: { customSuffix: "w", $sessionCountType: "word" /* Word */ }, folderCountType2: "none" /* None */, folderCountConfig2: { $sessionCountType: "word" /* Word */ }, folderCountType3: "none" /* None */, folderCountConfig3: { $sessionCountType: "word" /* Word */ }, folderPipeSeparator: "|", folderAbbreviateDescriptions: false, folderAlignment: "inline" /* Inline */, // ROOT showSameCountsOnRoot: true, rootCountType: "word" /* Word */, rootCountConfig: { customSuffix: "w", $sessionCountType: "word" /* Word */ }, rootCountType2: "none" /* None */, rootCountConfig2: { $sessionCountType: "word" /* Word */ }, rootCountType3: "none" /* None */, rootCountConfig3: { $sessionCountType: "word" /* Word */ }, rootPipeSeparator: "|", rootAbbreviateDescriptions: false, // ADVANCED showAdvanced: false, labelOpacity: 0.75, wordsPerMinute: 265, charsPerMinute: 500, wordsPerPage: 300, charsPerPage: 1500, charsPerPageIncludesWhitespace: false, characterCountType: "AllCharacters" /* StringLength */, pageCountType: "ByWords" /* ByWords */, includeDirectories: "", excludeComments: false, excludeCodeBlocks: false, excludeNonVisibleLinkPortions: false, excludeFootnotes: false, momentDateFormat: "", debugMode: false }; // logic/parser.ts var cjkRegex = /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/gu; var allSymbolsRegex = /[\p{S}\p{P}]/gu; function countMarkdown(content, config) { content = removeNonCountedContent(content, config); let wordSequences = content.replace(cjkRegex, " ").replace(allSymbolsRegex, "").trim().split(/\s+/); if (wordSequences.length === 1 && wordSequences[0] === "") { wordSequences = []; } let lineSequences = content.split("\n"); if (lineSequences.length === 1 && lineSequences[0] === "") { lineSequences = []; } const result = { charCount: content.length, nonWhitespaceCharCount: countNonWhitespaceCharacters(content), spaceDelimitedWordCount: wordSequences.length, cjkWordCount: (content.match(cjkRegex) || []).length, newlineCount: lineSequences.length }; return result; } var whitespaceRegex = /\s/g; function countNonWhitespaceCharacters(content) { return content.replace(whitespaceRegex, "").length; } function removeNonCountedContent(content, config) { if (config.excludeCodeBlocks) { content = content.replace(/(```.+?```)/gims, ""); } if (config.excludeComments) { content = content.replace(/(%%.+?%%|)/gims, ""); } if (config.excludeNonVisibleLinkPortions) { content = content.replace(/\[([^\]]*?)\]\([^\)]*?\)/gim, "$1"); content = content.replace(/\[\[(.*?)\]\]/gim, (_, $1) => { return !$1 ? "" : $1.includes("|") ? $1.slice($1.indexOf("|") + 1) : $1; }); } if (config.excludeFootnotes) { content = content.replace(/\[\^.+?\]: .*/gim, ""); content = content.replace(/\[\^.+?\]/gim, ""); } return content; } // logic/canvas.ts var CanvasHelper = class { constructor(debug) { this.debug = debug; } getCanvasText(file, content) { try { const canvas = JSON.parse(content); const texts = canvas.nodes.map((node) => node.text).filter((text) => !!text); return texts.join("\n"); } catch (ex) { this.debug.error(`Unable to parse canvas file [${file.name}]: ${ex}`); return ""; } } }; // logic/file.ts var FileHelper = class { constructor(app, plugin) { this.app = app; this.plugin = plugin; this.debugHelper = new DebugHelper(); this.canvasHelper = new CanvasHelper(this.debugHelper); this.pathIncludeMatchers = []; this.pathExcludeMatchers = []; this.FileTypeAllowlist = /* @__PURE__ */ new Set([ "", // Markdown extensions "markdown", "md", "mdml", "mdown", "mdtext", "mdtxt", "mdwn", "mkd", "mkdn", // Obsidian canvas "canvas", // Text files "txt", "text", "rtf", // MD with embedded code "qmd", "rmd", // MD for screenwriters "fountain", // LaTeX files "tex" ]); } get settings() { return this.plugin.settings; } get vault() { return this.app.vault; } async initializeAllFileCounts(cancellationToken) { const debugEnd = this.debugHelper.debugStart("getAllFileCounts"); const files = this.vault.getFiles(); if (typeof this.plugin.settings.includeDirectories === "string" && this.plugin.settings.includeDirectories.trim() !== "*" && this.plugin.settings.includeDirectories.trim() !== "") { const allMatchers = this.plugin.settings.includeDirectories.trim().split(",").map((matcher) => matcher.trim()); const includeMatchers = allMatchers.filter( (matcher) => !matcher.startsWith("!") ); const excludeMatchers = allMatchers.filter((matcher) => matcher.startsWith("!")).map((matcher) => matcher.slice(1)); const matchedFiles = files.filter( (file) => (includeMatchers.length === 0 ? true : includeMatchers.some((matcher) => file.path.includes(matcher))) && !excludeMatchers.some((matcher) => file.path.includes(matcher)) ); if (matchedFiles.length > 0) { this.pathIncludeMatchers = includeMatchers; this.pathExcludeMatchers = excludeMatchers; } else { this.pathIncludeMatchers = []; this.pathExcludeMatchers = []; this.debugHelper.debug( "No files matched by includeDirectories setting. Defaulting to all files." ); } } const counts = {}; for (const file of files) { if (cancellationToken.isCancelled) { break; } this.setCounts(counts, file, true); } debugEnd(); return counts; } getCachedDataForPath(counts, path) { if (counts.hasOwnProperty(path)) { return counts[path]; } const childPaths = this.getChildPaths(counts, path); const directoryDefault = { isCountable: false, targetNodeType: this.isRoot(path) ? "root" /* Root */ : "directory" /* Directory */, noteCount: 0, wordCount: 0, wordCountTowardGoal: 0, wordGoal: 0, pageCount: 0, characterCount: 0, nonWhitespaceCharacterCount: 0, newlineCount: 0, readingTimeInMinutes: 0, linkCount: 0, embedCount: 0, aliases: null, createdDate: 0, modifiedDate: 0, sizeInBytes: 0, sessionStart: { noteCount: 0, pageCount: 0, wordCount: 0, characterCount: 0, nonWhitespaceCharacterCount: 0, newlineCount: 0 } }; return childPaths.reduce((total, childPath) => { const childCount = this.getCachedDataForPath(counts, childPath); return { isCountable: total.isCountable || childCount.isCountable, targetNodeType: total.targetNodeType, noteCount: total.noteCount + childCount.noteCount, linkCount: total.linkCount + childCount.linkCount, embedCount: total.embedCount + childCount.embedCount, aliases: [], wordCount: total.wordCount + childCount.wordCount, wordCountTowardGoal: total.wordCountTowardGoal + childCount.wordCountTowardGoal, wordGoal: total.wordGoal + childCount.wordGoal, pageCount: total.pageCount + childCount.pageCount, characterCount: total.characterCount + childCount.characterCount, nonWhitespaceCharacterCount: total.nonWhitespaceCharacterCount + childCount.nonWhitespaceCharacterCount, newlineCount: total.newlineCount + childCount.newlineCount, readingTimeInMinutes: total.readingTimeInMinutes + childCount.readingTimeInMinutes, createdDate: total.createdDate === 0 ? childCount.createdDate : Math.min(total.createdDate, childCount.createdDate), modifiedDate: Math.max(total.modifiedDate, childCount.modifiedDate), sizeInBytes: total.sizeInBytes + childCount.sizeInBytes, sessionStart: { noteCount: total.sessionStart.noteCount + childCount.sessionStart.noteCount, pageCount: total.sessionStart.pageCount + childCount.sessionStart.pageCount, wordCount: total.sessionStart.wordCount + childCount.sessionStart.wordCount, characterCount: total.sessionStart.characterCount + childCount.sessionStart.characterCount, nonWhitespaceCharacterCount: total.sessionStart.nonWhitespaceCharacterCount + childCount.sessionStart.nonWhitespaceCharacterCount, newlineCount: total.sessionStart.newlineCount + childCount.sessionStart.newlineCount } }; }, directoryDefault); } setDebugMode(debug) { this.debugHelper.setDebugMode(debug); } removeFileCounts(path, counts) { this.removeCounts(counts, path); for (const childPath of this.getChildPaths(counts, path)) { this.removeCounts(counts, childPath); } } async updateFileCounts(abstractFile, counts, cancellationToken) { if (abstractFile instanceof import_obsidian2.TFolder) { for (const child of abstractFile.children) { await this.updateFileCounts(child, counts, cancellationToken); } return; } if (abstractFile instanceof import_obsidian2.TFile) { await this.setCounts(counts, abstractFile, false); } } countEmbeds(metadata) { var _a, _b; return (_b = (_a = metadata == null ? void 0 : metadata.embeds) == null ? void 0 : _a.length) != null ? _b : 0; } countLinks(metadata) { var _a, _b; return (_b = (_a = metadata == null ? void 0 : metadata.links) == null ? void 0 : _a.length) != null ? _b : 0; } getChildPaths(counts, path) { const childPaths = Object.keys(counts).filter( (countPath) => path === "/" || countPath.startsWith(path + "/") ); return childPaths; } isRoot(path) { return !path || path === "/"; } removeCounts(counts, path) { delete counts[path]; } async setCounts(counts, file, startNewSession) { var _a, _b; const metadata = this.app.metadataCache.getFileCache( file ); const shouldCountFile = this.shouldCountFile(file, metadata); const existingSession = (_a = counts[file.path]) == null ? void 0 : _a.sessionStart; counts[file.path] = { isCountable: shouldCountFile, targetNodeType: "file" /* File */, noteCount: 0, wordCount: 0, wordCountTowardGoal: 0, wordGoal: 0, pageCount: 0, characterCount: 0, nonWhitespaceCharacterCount: 0, newlineCount: 0, readingTimeInMinutes: 0, linkCount: 0, embedCount: 0, aliases: [], createdDate: file.stat.ctime, modifiedDate: file.stat.mtime, sizeInBytes: file.stat.size, sessionStart: startNewSession || !existingSession ? { noteCount: 0, pageCount: 0, wordCount: 0, characterCount: 0, nonWhitespaceCharacterCount: 0, newlineCount: 0 } : existingSession }; if (!shouldCountFile) { return; } let content = await this.vault.cachedRead(file); if (file.extension.toLowerCase() === "canvas") { content = this.canvasHelper.getCanvasText(file, content); } else { content = this.trimFrontmatter(content, metadata); } const countResult = countMarkdown(content, { excludeCodeBlocks: this.settings.excludeCodeBlocks, excludeComments: this.settings.excludeComments, excludeNonVisibleLinkPortions: this.settings.excludeNonVisibleLinkPortions, excludeFootnotes: this.settings.excludeFootnotes }); const combinedWordCount = countResult.cjkWordCount + countResult.spaceDelimitedWordCount; const wordGoal = this.getWordGoal(metadata); const cjkReadingTime = countResult.cjkWordCount / (this.settings.charsPerMinute || 500); const spaceDelimitedReadingTime = countResult.spaceDelimitedWordCount / (this.settings.wordsPerMinute || 265); const readingTimeInMinutes = cjkReadingTime + spaceDelimitedReadingTime; let pageCount = 0; if (this.settings.pageCountType === "ByWords" /* ByWords */) { const wordsPerPage = Number(this.settings.wordsPerPage); const wordsPerPageValid = !isNaN(wordsPerPage) && wordsPerPage > 0; pageCount = combinedWordCount / (wordsPerPageValid ? wordsPerPage : 300); } else if (this.settings.pageCountType === "ByChars" /* ByChars */ && !this.settings.charsPerPageIncludesWhitespace) { const charsPerPage = Number(this.settings.charsPerPage); const charsPerPageValid = !isNaN(charsPerPage) && charsPerPage > 0; pageCount = countResult.nonWhitespaceCharCount / (charsPerPageValid ? charsPerPage : 1500); } else if (this.settings.pageCountType === "ByChars" /* ByChars */ && this.settings.charsPerPageIncludesWhitespace) { const charsPerPage = Number(this.settings.charsPerPage); const charsPerPageValid = !isNaN(charsPerPage) && charsPerPage > 0; pageCount = countResult.charCount / (charsPerPageValid ? charsPerPage : 1500); } Object.assign(counts[file.path], { noteCount: 1, wordCount: combinedWordCount, wordCountTowardGoal: wordGoal !== null ? combinedWordCount : 0, wordGoal, pageCount, characterCount: countResult.charCount, nonWhitespaceCharacterCount: countResult.nonWhitespaceCharCount, newlineCount: countResult.newlineCount, readingTimeInMinutes, linkCount: this.countLinks(metadata), embedCount: this.countEmbeds(metadata), aliases: (0, import_obsidian2.parseFrontMatterAliases)(metadata == null ? void 0 : metadata.frontmatter), frontmatter: metadata == null ? void 0 : metadata.frontmatter, sessionStart: { ...(_b = counts[file.path]) == null ? void 0 : _b.sessionStart, ...startNewSession ? { noteCount: 1, pageCount, wordCount: combinedWordCount, characterCount: countResult.charCount, nonWhitespaceCharacterCount: countResult.nonWhitespaceCharCount, newlineCount: countResult.newlineCount } : {} } }); } getWordGoal(metadata) { const goal = metadata && metadata.frontmatter && metadata.frontmatter["word-goal"]; if (!goal || isNaN(Number(goal))) { return null; } return Number(goal); } trimFrontmatter(content, metadata) { let meaningfulContent = content; const hasFrontmatter = !!metadata && !!metadata.frontmatter; if (hasFrontmatter) { const frontmatterPos = metadata.frontmatterPosition || metadata.frontmatter.position; meaningfulContent = frontmatterPos && frontmatterPos.start && frontmatterPos.end ? meaningfulContent.slice(0, frontmatterPos.start.offset) + meaningfulContent.slice(frontmatterPos.end.offset) : meaningfulContent; } return meaningfulContent; } shouldCountFile(file, metadata) { if (this.pathIncludeMatchers.length > 0 && !this.pathIncludeMatchers.some((matcher) => file.path.includes(matcher))) { return false; } if (this.pathExcludeMatchers.length > 0 && this.pathExcludeMatchers.some((matcher) => file.path.includes(matcher))) { return false; } if (!this.FileTypeAllowlist.has(file.extension.toLowerCase())) { return false; } if (!metadata) { return true; } if (metadata.frontmatter && metadata.frontmatter.hasOwnProperty("wordcount") && (metadata.frontmatter.wordcount === null || metadata.frontmatter.wordcount === false || metadata.frontmatter.wordcount === "false")) { return false; } const tags = (0, import_obsidian2.getAllTags)(metadata).map((tag) => tag.toLowerCase()); if (tags.length && (tags.includes("#excalidraw") || tags.filter((tag) => tag.startsWith("#exclude")).map((tag) => tag.replace(/[-_]/g, "")).includes("#excludefromwordcount"))) { return false; } return true; } }; // logic/locale_format.ts var locales = [...navigator.languages, "en-US"]; var NumberFormatDefault = new Intl.NumberFormat(locales); var NumberFormatDecimal = new Intl.NumberFormat(locales, { minimumFractionDigits: 1, maximumFractionDigits: 2 }); var NumberFormatFileSize = new Intl.NumberFormat(locales, { minimumFractionDigits: 0, maximumFractionDigits: 2 }); // logic/filesize.ts var formatThresholds = [{ suffix: "B", suffixLong: " B", divisor: 1 }, { suffix: "kB", suffixLong: " kB", divisor: 1e3 }, { suffix: "MB", suffixLong: " MB", divisor: 1e6 }, { suffix: "GB", suffixLong: " GB", divisor: 1e9 }, { suffix: "TB", suffixLong: " TB", divisor: 1e12 }]; var FileSizeHelper = class { formatFileSize(bytes, shouldAbbreviate) { const largestThreshold = formatThresholds.last(); for (const formatThreshold of formatThresholds) { if (bytes < formatThreshold.divisor * 1e3 || formatThreshold === largestThreshold) { const units = bytes / formatThreshold.divisor; const suffix = shouldAbbreviate ? formatThreshold.suffix : formatThreshold.suffixLong; return `${NumberFormatFileSize.format(units)}${suffix}`; } } return `?B`; } }; // logic/readtime.ts var ReadTimeHelper = class { formatReadTime(minutes, shouldAbbreviate) { const final = shouldAbbreviate ? "" : " read"; if (minutes * 60 < 1) { return `0m${final}`; } if (minutes < 1) { const seconds = Math.round(minutes * 60); return `${seconds}s${final}`; } if (minutes < 60) { return `${Math.round(minutes)}m${final}`; } const hours = NumberFormatDefault.format(Math.floor(minutes / 60)); const remainder = Math.floor(minutes) % 60; return remainder === 0 ? `${hours}h${final}` : `${hours}h${remainder}m${final}`; } }; // logic/node_label.ts var import_obsidian3 = require("obsidian"); var NodeLabelHelper = class { constructor(plugin) { this.plugin = plugin; this.fileSizeHelper = new FileSizeHelper(); this.readTimeHelper = new ReadTimeHelper(); this.unconditionalCountTypes = [ "created" /* Created */, "filesize" /* FileSize */, "modified" /* Modified */ ]; } get settings() { return this.plugin.settings; } getNodeLabel(counts) { let countTypes; let abbreviateDescriptions; let separator; const noteCountTypes = [ this.getCountTypeWithSuffix( this.settings.countType, this.settings.countConfig ), this.getCountTypeWithSuffix( this.settings.countType2, this.settings.countConfig2 ), this.getCountTypeWithSuffix( this.settings.countType3, this.settings.countConfig3 ) ]; const noteAbbreviateDescriptions = this.settings.abbreviateDescriptions; const noteSeparator = this.settings.useAdvancedFormatting ? this.settings.pipeSeparator : "|"; switch (counts.targetNodeType) { case "root" /* Root */: if (this.settings.showSameCountsOnRoot) { countTypes = noteCountTypes; abbreviateDescriptions = noteAbbreviateDescriptions; separator = noteSeparator; break; } countTypes = [ this.getCountTypeWithSuffix( this.settings.rootCountType, this.settings.rootCountConfig ), this.getCountTypeWithSuffix( this.settings.rootCountType2, this.settings.rootCountConfig2 ), this.getCountTypeWithSuffix( this.settings.rootCountType3, this.settings.rootCountConfig3 ) ]; abbreviateDescriptions = this.settings.rootAbbreviateDescriptions; separator = this.settings.useAdvancedFormatting ? this.settings.rootPipeSeparator : noteSeparator; break; case "directory" /* Directory */: if (this.settings.showSameCountsOnFolders) { countTypes = noteCountTypes; abbreviateDescriptions = noteAbbreviateDescriptions; separator = noteSeparator; break; } countTypes = [ this.getCountTypeWithSuffix( this.settings.folderCountType, this.settings.folderCountConfig ), this.getCountTypeWithSuffix( this.settings.folderCountType2, this.settings.folderCountConfig2 ), this.getCountTypeWithSuffix( this.settings.folderCountType3, this.settings.folderCountConfig3 ) ]; abbreviateDescriptions = this.settings.folderAbbreviateDescriptions; separator = this.settings.useAdvancedFormatting ? this.settings.folderPipeSeparator : noteSeparator; break; default: countTypes = noteCountTypes; abbreviateDescriptions = noteAbbreviateDescriptions; separator = noteSeparator; break; } return countTypes.filter((ct) => ct.$countType !== "none" /* None */).map( (ct) => this.getDataTypeLabel( counts, ct, abbreviateDescriptions ) ).filter((display) => display !== null).join(` ${separator} `); } getCountTypeWithSuffix($countType, countConfig) { return { $countType, customSuffix: this.settings.useAdvancedFormatting ? countConfig.customSuffix : null, frontmatterKey: countConfig.frontmatterKey, $sessionCountType: countConfig.$sessionCountType }; } getBasicCountString(config) { var _a; const defaultSuffix = config.abbreviateDescriptions ? config.abbreviatedNoun : ` ${config.noun}${config.count == "1" ? "" : "s"}`; const suffix = (_a = config.customSuffix) != null ? _a : defaultSuffix; return `${config.count}${suffix}`; } getDataTypeLabel(counts, config, abbreviateDescriptions) { var _a, _b; if (!counts || typeof counts.wordCount !== "number") { return null; } if (!counts.isCountable && !this.unconditionalCountTypes.includes(config.$countType)) { return null; } switch (config.$countType) { case "none" /* None */: return null; case "word" /* Word */: return this.getBasicCountString({ count: NumberFormatDefault.format(Math.ceil(counts.wordCount)), noun: "word", abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["word" /* Word */], abbreviateDescriptions, customSuffix: config.customSuffix }); case "page" /* Page */: return this.getBasicCountString({ count: NumberFormatDefault.format(Math.ceil(counts.pageCount)), noun: "page", abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["page" /* Page */], abbreviateDescriptions, customSuffix: config.customSuffix }); case "pagedecimal" /* PageDecimal */: return this.getBasicCountString({ count: NumberFormatDecimal.format(counts.pageCount), noun: "page", abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["pagedecimal" /* PageDecimal */], abbreviateDescriptions, customSuffix: config.customSuffix }); case "linebreak" /* Linebreak */: return this.getBasicCountString({ count: NumberFormatDefault.format(counts.newlineCount), noun: "line", abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["linebreak" /* Linebreak */], abbreviateDescriptions, customSuffix: config.customSuffix }); case "percentgoal" /* PercentGoal */: { if (counts.wordGoal <= 0) { return null; } const fraction = counts.wordCountTowardGoal / counts.wordGoal; const percent = NumberFormatDefault.format(Math.round(fraction * 100)); const defaultSuffix = abbreviateDescriptions ? "%" : `% of ${NumberFormatDefault.format(counts.wordGoal)}`; const suffix = (_a = config.customSuffix) != null ? _a : defaultSuffix; return `${percent}${suffix}`; } case "note" /* Note */: return this.getBasicCountString({ count: NumberFormatDefault.format(counts.noteCount), noun: "note", abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["note" /* Note */], abbreviateDescriptions, customSuffix: config.customSuffix }); case "character" /* Character */: { const characterCount = this.settings.characterCountType === "ExcludeWhitespace" /* ExcludeWhitespace */ ? counts.nonWhitespaceCharacterCount : counts.characterCount; return this.getBasicCountString({ count: NumberFormatDefault.format(characterCount), noun: "character", abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["character" /* Character */], abbreviateDescriptions, customSuffix: config.customSuffix }); } case "readtime" /* ReadTime */: return this.readTimeHelper.formatReadTime( counts.readingTimeInMinutes, abbreviateDescriptions ); case "link" /* Link */: if (counts.linkCount === 0) { return null; } return this.getBasicCountString({ count: NumberFormatDefault.format(counts.linkCount), noun: "link", abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["link" /* Link */], abbreviateDescriptions, customSuffix: config.customSuffix }); case "embed" /* Embed */: if (counts.embedCount === 0) { return null; } return this.getBasicCountString({ count: NumberFormatDefault.format(counts.embedCount), noun: "embed", abbreviatedNoun: COUNT_TYPE_DEFAULT_SHORT_SUFFIXES["embed" /* Embed */], abbreviateDescriptions, customSuffix: config.customSuffix }); case "alias" /* Alias */: if (!counts.aliases || !Array.isArray(counts.aliases) || !counts.aliases.length) { return null; } return abbreviateDescriptions ? `${counts.aliases[0]}` : `alias: ${counts.aliases[0]}${counts.aliases.length > 1 ? ` +${counts.aliases.length - 1}` : ""}`; case "created" /* Created */: { if (counts.createdDate === 0) { return null; } const cDate = (0, import_obsidian3.moment)(counts.createdDate).format(this.settings.momentDateFormat || "YYYY/MM/DD"); if (config.customSuffix !== null) { return `${cDate}${config.customSuffix}`; } return abbreviateDescriptions ? `${cDate}/c` : `Created ${cDate}`; } case "modified" /* Modified */: { if (counts.modifiedDate === 0) { return null; } const uDate = (0, import_obsidian3.moment)(counts.modifiedDate).format(this.settings.momentDateFormat || "YYYY/MM/DD"); if (config.customSuffix !== null) { return `${uDate}${config.customSuffix}`; } return abbreviateDescriptions ? `${uDate}/u` : `Updated ${uDate}`; } case "filesize" /* FileSize */: return this.fileSizeHelper.formatFileSize( counts.sizeInBytes, abbreviateDescriptions ); case "frontmatterKey" /* FrontmatterKey */: { if (!config.frontmatterKey) { return null; } const value = (_b = counts == null ? void 0 : counts.frontmatter) == null ? void 0 : _b[config.frontmatterKey]; if (value === void 0 || value === null) { return null; } if (config.customSuffix !== null) { return `${value}${config.customSuffix}`; } return value; } case "tracksession" /* TrackSession */: { if (!config.$sessionCountType) { return null; } const quantity = this.getSessionQuantity(counts, config.$sessionCountType); if (config.customSuffix !== null) { return `${quantity}${config.customSuffix}`; } return abbreviateDescriptions ? `${quantity}/s` : `Session: ${quantity}`; } } return null; } getSessionQuantity(counts, $countType) { switch ($countType) { case "word" /* Word */: return NumberFormatDefault.format(Math.ceil(counts.wordCount - counts.sessionStart.wordCount)); case "page" /* Page */: return NumberFormatDefault.format(Math.ceil(counts.pageCount - counts.sessionStart.pageCount)); case "pagedecimal" /* PageDecimal */: return NumberFormatDecimal.format(counts.pageCount - counts.sessionStart.pageCount); case "linebreak" /* Linebreak */: return NumberFormatDefault.format(counts.newlineCount - counts.sessionStart.newlineCount); case "note" /* Note */: return NumberFormatDefault.format(counts.noteCount - counts.sessionStart.noteCount); case "character" /* Character */: { const characterCount = this.settings.characterCountType === "ExcludeWhitespace" /* ExcludeWhitespace */ ? counts.nonWhitespaceCharacterCount : counts.characterCount; const startingCharacterCount = this.settings.characterCountType === "ExcludeWhitespace" /* ExcludeWhitespace */ ? counts.sessionStart.nonWhitespaceCharacterCount : counts.sessionStart.characterCount; return NumberFormatDefault.format(characterCount - startingCharacterCount); } } } }; // logic/saved_data.ts var SavedDataHelper = class { constructor(plugin) { this.plugin = plugin; } async getSavedData() { const loaded = await this.plugin.loadData(); const denulled = Object.assign({}, loaded); denulled.settings = Object.assign({}, DEFAULT_SETTINGS, denulled.settings); const migrated = migrateSavedData(denulled); return migrated; } }; function migrateSavedData(saved) { const migrations = [ overwriteInvalidCountTypes, migrateToCountConfigurationObject ]; for (const migrate of migrations) { saved = migrate(saved); } return saved; } var overwriteInvalidCountTypes = (saved) => { var _a; if (!((_a = saved == null ? void 0 : saved.settings) == null ? void 0 : _a.countType)) { return; } const fieldsToCheck = [ "countType", "countType2", "countType3", "folderCountType", "folderCountType2", "folderCountType3", "rootCountType", "rootCountType2", "rootCountType3" ]; for (const field of fieldsToCheck) { if (!COUNT_TYPES.includes(saved.settings[field])) { saved.settings[field] = field === "countType" ? "word" /* Word */ : "none" /* None */; } } return saved; }; var migrateToCountConfigurationObject = (saved) => { var _a, _b, _c, _d, _e, _f, _g, _h, _i; const settings = saved.settings; const oldSettings = saved.settings; (_a = settings.countConfig) != null ? _a : settings.countConfig = {}; if (typeof oldSettings.countTypeSuffix === "string") { settings.countConfig.customSuffix = oldSettings.countTypeSuffix; delete oldSettings.countTypeSuffix; } if (typeof oldSettings.frontmatterKey === "string") { settings.countConfig.frontmatterKey = oldSettings.frontmatterKey; delete oldSettings.frontmatterKey; } (_b = settings.countConfig2) != null ? _b : settings.countConfig2 = {}; if (typeof oldSettings.countType2Suffix === "string") { settings.countConfig2.customSuffix = oldSettings.countType2Suffix; delete oldSettings.countType2Suffix; } if (typeof oldSettings.frontmatterKey2 === "string") { settings.countConfig2.frontmatterKey = oldSettings.frontmatterKey2; delete oldSettings.frontmatterKey2; } (_c = settings.countConfig3) != null ? _c : settings.countConfig3 = {}; if (typeof oldSettings.countType3Suffix === "string") { settings.countConfig3.customSuffix = oldSettings.countType3Suffix; delete oldSettings.countType3Suffix; } if (typeof oldSettings.frontmatterKey3 === "string") { settings.countConfig3.frontmatterKey = oldSettings.frontmatterKey3; delete oldSettings.frontmatterKey3; } (_d = settings.folderCountConfig) != null ? _d : settings.folderCountConfig = {}; if (typeof oldSettings.folderCountTypeSuffix === "string") { settings.folderCountConfig.customSuffix = oldSettings.folderCountTypeSuffix; delete oldSettings.folderCountTypeSuffix; } (_e = settings.folderCountConfig2) != null ? _e : settings.folderCountConfig2 = {}; if (typeof oldSettings.folderCountType2Suffix === "string") { settings.folderCountConfig2.customSuffix = oldSettings.folderCountType2Suffix; delete oldSettings.folderCountType2Suffix; } (_f = settings.folderCountConfig3) != null ? _f : settings.folderCountConfig3 = {}; if (typeof oldSettings.folderCountType3Suffix === "string") { settings.folderCountConfig3.customSuffix = oldSettings.folderCountType3Suffix; delete oldSettings.folderCountType3Suffix; } (_g = settings.rootCountConfig) != null ? _g : settings.rootCountConfig = {}; if (typeof oldSettings.rootCountTypeSuffix === "string") { settings.rootCountConfig.customSuffix = oldSettings.rootCountTypeSuffix; delete oldSettings.rootCountTypeSuffix; } (_h = settings.rootCountConfig2) != null ? _h : settings.rootCountConfig2 = {}; if (typeof oldSettings.rootCountType2Suffix === "string") { settings.rootCountConfig2.customSuffix = oldSettings.rootCountType2Suffix; delete oldSettings.rootCountType2Suffix; } (_i = settings.rootCountConfig3) != null ? _i : settings.rootCountConfig3 = {}; if (typeof oldSettings.rootCountType3Suffix === "string") { settings.rootCountConfig3.customSuffix = oldSettings.rootCountType3Suffix; delete oldSettings.rootCountType3Suffix; } return saved; }; // logic/settings.tab.ts var import_obsidian4 = require("obsidian"); var NovelWordCountSettingTab = class extends import_obsidian4.PluginSettingTab { constructor(app, plugin) { super(app, plugin); this.plugin = plugin; } display() { const { containerEl } = this; containerEl.empty(); this.renderNoteSettings(containerEl); this.renderFolderSettings(containerEl); this.renderRootSettings(containerEl); this.renderAdvancedSettings(containerEl); this.renderReanalyzeButton(containerEl); this.renderDonationButton(containerEl); } // // NOTES // renderNoteSettings(containerEl) { const mainHeader = containerEl.createEl("div", { cls: [ "setting-item", "setting-item-heading", "novel-word-count-settings-header" ] }); mainHeader.createEl("div", { text: "Notes" }); mainHeader.createEl("div", { text: "You can display up to three data types side by side.", cls: "setting-item-description" }); new import_obsidian4.Setting(containerEl).setDesc("Use advanced formatting").addToggle( (toggle) => toggle.setValue(this.plugin.settings.useAdvancedFormatting).onChange(async (value) => { this.plugin.settings.useAdvancedFormatting = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); this.display(); }) ); this.renderCountTypeSetting(containerEl, { name: "1st data type to show", oldCountType: this.plugin.settings.countType, setNewCountType: (value) => { this.plugin.settings.countType = value; this.plugin.settings.countConfig.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.countType]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.countType, oldCountType: this.plugin.settings.countConfig.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.countConfig.$sessionCountType = value; } }); this.renderFrontmatterKeySetting(containerEl, { countType: this.plugin.settings.countType, oldKey: this.plugin.settings.countConfig.frontmatterKey, setNewKey: (value) => this.plugin.settings.countConfig.frontmatterKey = value }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.countType, oldSuffix: this.plugin.settings.countConfig.customSuffix, setNewSuffix: (value) => this.plugin.settings.countConfig.customSuffix = value }); this.renderCountTypeSetting(containerEl, { name: "2nd data type to show", oldCountType: this.plugin.settings.countType2, setNewCountType: (value) => { this.plugin.settings.countType2 = value; this.plugin.settings.countConfig2.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.countType2]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.countType2, oldCountType: this.plugin.settings.countConfig2.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.countConfig2.$sessionCountType = value; } }); this.renderFrontmatterKeySetting(containerEl, { countType: this.plugin.settings.countType2, oldKey: this.plugin.settings.countConfig2.frontmatterKey, setNewKey: (value) => this.plugin.settings.countConfig2.frontmatterKey = value }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.countType2, oldSuffix: this.plugin.settings.countConfig2.customSuffix, setNewSuffix: (value) => this.plugin.settings.countConfig2.customSuffix = value }); this.renderCountTypeSetting(containerEl, { name: "3rd data type to show", oldCountType: this.plugin.settings.countType3, setNewCountType: (value) => { this.plugin.settings.countType3 = value; this.plugin.settings.countConfig3.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.countType3]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.countType3, oldCountType: this.plugin.settings.countConfig3.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.countConfig3.$sessionCountType = value; } }); this.renderFrontmatterKeySetting(containerEl, { countType: this.plugin.settings.countType3, oldKey: this.plugin.settings.countConfig3.frontmatterKey, setNewKey: (value) => this.plugin.settings.countConfig3.frontmatterKey = value }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.countType3, oldSuffix: this.plugin.settings.countConfig3.customSuffix, setNewSuffix: (value) => this.plugin.settings.countConfig3.customSuffix = value }); if (this.plugin.settings.useAdvancedFormatting) { new import_obsidian4.Setting(containerEl).setName("Data type separator").addText( (text) => text.setValue(this.plugin.settings.pipeSeparator).onChange(async (value) => { this.plugin.settings.pipeSeparator = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }) ); } if (!this.plugin.settings.useAdvancedFormatting) { new import_obsidian4.Setting(containerEl).setName("Abbreviate descriptions").setDesc("E.g. show '120w' instead of '120 words'").addToggle( (toggle) => toggle.setValue(this.plugin.settings.abbreviateDescriptions).onChange(async (value) => { this.plugin.settings.abbreviateDescriptions = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }) ); } new import_obsidian4.Setting(containerEl).setName("Alignment").setDesc( "Show data inline with file/folder names, right-aligned, or underneath" ).addDropdown((drop) => { drop.addOption("inline" /* Inline */, "Inline").addOption("right" /* Right */, "Right-aligned").addOption("below" /* Below */, "Below").setValue(this.plugin.settings.alignment).onChange(async (value) => { this.plugin.settings.alignment = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }); }); } renderFolderSettings(containerEl) { this.renderSeparator(containerEl); new import_obsidian4.Setting(containerEl).setHeading().setName("Folders: Same data as Notes").addToggle( (toggle) => toggle.setValue(this.plugin.settings.showSameCountsOnFolders).onChange(async (value) => { this.plugin.settings.showSameCountsOnFolders = value; await this.plugin.saveSettings(); this.display(); await this.plugin.updateDisplayedCounts(); }) ); if (!this.plugin.settings.showSameCountsOnFolders) { this.renderCountTypeSetting(containerEl, { name: "1st data type to show", oldCountType: this.plugin.settings.folderCountType, setNewCountType: (value) => { this.plugin.settings.folderCountType = value; this.plugin.settings.folderCountConfig.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.folderCountType]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.folderCountType, oldCountType: this.plugin.settings.folderCountConfig.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.folderCountConfig.$sessionCountType = value; } }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.folderCountType, oldSuffix: this.plugin.settings.folderCountConfig.customSuffix, setNewSuffix: (value) => this.plugin.settings.folderCountConfig.customSuffix = value }); this.renderCountTypeSetting(containerEl, { name: "2nd data type to show", oldCountType: this.plugin.settings.folderCountType2, setNewCountType: (value) => { this.plugin.settings.folderCountType2 = value; this.plugin.settings.folderCountConfig2.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.folderCountType2]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.folderCountType2, oldCountType: this.plugin.settings.folderCountConfig2.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.folderCountConfig2.$sessionCountType = value; } }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.folderCountType2, oldSuffix: this.plugin.settings.folderCountConfig2.customSuffix, setNewSuffix: (value) => this.plugin.settings.folderCountConfig2.customSuffix = value }); this.renderCountTypeSetting(containerEl, { name: "3rd data type to show", oldCountType: this.plugin.settings.folderCountType3, setNewCountType: (value) => { this.plugin.settings.folderCountType3 = value; this.plugin.settings.folderCountConfig3.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.folderCountType3]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.folderCountType3, oldCountType: this.plugin.settings.folderCountConfig3.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.folderCountConfig3.$sessionCountType = value; } }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.folderCountType3, oldSuffix: this.plugin.settings.folderCountConfig3.customSuffix, setNewSuffix: (value) => this.plugin.settings.folderCountConfig3.customSuffix = value }); if (this.plugin.settings.useAdvancedFormatting) { new import_obsidian4.Setting(containerEl).setName("Data type separator").addText( (text) => text.setValue(this.plugin.settings.folderPipeSeparator).onChange(async (value) => { this.plugin.settings.folderPipeSeparator = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }) ); } if (!this.plugin.settings.useAdvancedFormatting) { new import_obsidian4.Setting(containerEl).setName("Abbreviate descriptions").addToggle( (toggle) => toggle.setValue(this.plugin.settings.folderAbbreviateDescriptions).onChange(async (value) => { this.plugin.settings.folderAbbreviateDescriptions = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }) ); } new import_obsidian4.Setting(containerEl).setName("Alignment").addDropdown((drop) => { drop.addOption("inline" /* Inline */, "Inline").addOption("right" /* Right */, "Right-aligned").addOption("below" /* Below */, "Below").setValue(this.plugin.settings.folderAlignment).onChange(async (value) => { this.plugin.settings.folderAlignment = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }); }); } } renderRootSettings(containerEl) { this.renderSeparator(containerEl); new import_obsidian4.Setting(containerEl).setHeading().setName("Root: Same data as Notes").addToggle( (toggle) => toggle.setValue(this.plugin.settings.showSameCountsOnRoot).onChange(async (value) => { this.plugin.settings.showSameCountsOnRoot = value; await this.plugin.saveSettings(); this.display(); await this.plugin.updateDisplayedCounts(); }) ); if (!this.plugin.settings.showSameCountsOnRoot) { this.renderCountTypeSetting(containerEl, { name: "1st data type to show", oldCountType: this.plugin.settings.rootCountType, setNewCountType: (value) => { this.plugin.settings.rootCountType = value; this.plugin.settings.rootCountConfig.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.rootCountType]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.rootCountType, oldCountType: this.plugin.settings.rootCountConfig.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.rootCountConfig.$sessionCountType = value; } }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.rootCountType, oldSuffix: this.plugin.settings.rootCountConfig.customSuffix, setNewSuffix: (value) => this.plugin.settings.rootCountConfig.customSuffix = value }); this.renderCountTypeSetting(containerEl, { name: "2nd data type to show", oldCountType: this.plugin.settings.rootCountType2, setNewCountType: (value) => { this.plugin.settings.rootCountType2 = value; this.plugin.settings.rootCountConfig2.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.rootCountType2]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.rootCountType2, oldCountType: this.plugin.settings.rootCountConfig2.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.rootCountConfig2.$sessionCountType = value; } }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.rootCountType2, oldSuffix: this.plugin.settings.rootCountConfig2.customSuffix, setNewSuffix: (value) => this.plugin.settings.rootCountConfig2.customSuffix = value }); this.renderCountTypeSetting(containerEl, { name: "3rd data type to show", oldCountType: this.plugin.settings.rootCountType3, setNewCountType: (value) => { this.plugin.settings.rootCountType3 = value; this.plugin.settings.rootCountConfig3.customSuffix = COUNT_TYPE_DEFAULT_SHORT_SUFFIXES[this.plugin.settings.rootCountType3]; } }); this.renderSessionCountTypeSetting(containerEl, { parentCountType: this.plugin.settings.rootCountType3, oldCountType: this.plugin.settings.rootCountConfig3.$sessionCountType, setNewCountType: (value) => { this.plugin.settings.rootCountConfig3.$sessionCountType = value; } }); this.renderCustomFormatSetting(containerEl, { countType: this.plugin.settings.rootCountType3, oldSuffix: this.plugin.settings.rootCountConfig3.customSuffix, setNewSuffix: (value) => this.plugin.settings.rootCountConfig3.customSuffix = value }); if (this.plugin.settings.useAdvancedFormatting) { new import_obsidian4.Setting(containerEl).setName("Data type separator").addText( (text) => text.setValue(this.plugin.settings.rootPipeSeparator).onChange(async (value) => { this.plugin.settings.rootPipeSeparator = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }) ); } if (!this.plugin.settings.useAdvancedFormatting) { new import_obsidian4.Setting(containerEl).setName("Abbreviate descriptions").addToggle( (toggle) => toggle.setValue(this.plugin.settings.rootAbbreviateDescriptions).onChange(async (value) => { this.plugin.settings.rootAbbreviateDescriptions = value; await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }) ); } } } renderAdvancedSettings(containerEl) { this.renderSeparator(containerEl); new import_obsidian4.Setting(containerEl).setHeading().setName("Show advanced options").setDesc("Language compatibility and fine-tuning").addToggle( (toggle) => toggle.setValue(this.plugin.settings.showAdvanced).onChange(async (value) => { this.plugin.settings.showAdvanced = value; await this.plugin.saveSettings(); this.display(); }) ); if (this.plugin.settings.showAdvanced) { const opacityChanged = async (value) => { this.plugin.settings.labelOpacity = Math.clamp(value, 0, 1); await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }; new import_obsidian4.Setting(containerEl).setName("Label opacity").setDesc("Increase this value to make all count labels in the File Explorer more visible.").addSlider((slider) => { slider.setLimits(0, 1, 0.05).setDynamicTooltip().setValue(this.plugin.settings.labelOpacity).onChange((0, import_obsidian4.debounce)(opacityChanged.bind(this), 500)); }); const includePathsChanged = async (txt, value) => { this.plugin.settings.includeDirectories = value; await this.plugin.saveSettings(); await this.plugin.initialize(); }; new import_obsidian4.Setting(containerEl).setName("Include file/folder names").setDesc( "Only count paths matching the indicated term(s). Case-sensitive, comma-separated. Defaults to all files. Any term starting with ! will be excluded instead of included." ).addText((txt) => { txt.setPlaceholder("").setValue(this.plugin.settings.includeDirectories).onChange((0, import_obsidian4.debounce)(includePathsChanged.bind(this, txt), 1e3)); }); new import_obsidian4.Setting(containerEl).setName("Exclude comments").setDesc( "Exclude %%Obsidian%% and comments from counts. May affect performance on large vaults." ).addToggle( (toggle) => toggle.setValue(this.plugin.settings.excludeComments).onChange(async (value) => { this.plugin.settings.excludeComments = value; await this.plugin.saveSettings(); await this.plugin.initialize(); }) ); new import_obsidian4.Setting(containerEl).setName("Exclude code blocks").setDesc( "Exclude ```code blocks``` (e.g. DataView snippets) from all counts. May affect performance on large vaults." ).addToggle( (toggle) => toggle.setValue(this.plugin.settings.excludeCodeBlocks).onChange(async (value) => { this.plugin.settings.excludeCodeBlocks = value; await this.plugin.saveSettings(); await this.plugin.initialize(); }) ); new import_obsidian4.Setting(containerEl).setName("Exclude non-visible portions of links").setDesc( "For external links, exclude the URI from all counts. For internal links with aliases, only count the alias. May affect performance on large vaults." ).addToggle( (toggle) => toggle.setValue(this.plugin.settings.excludeNonVisibleLinkPortions).onChange(async (value) => { this.plugin.settings.excludeNonVisibleLinkPortions = value; await this.plugin.saveSettings(); await this.plugin.initialize(); }) ); new import_obsidian4.Setting(containerEl).setName("Exclude footnotes").setDesc( "Exclude footnotes[^1] from counts. May affect performance on large vaults." ).addToggle( (toggle) => toggle.setValue(this.plugin.settings.excludeFootnotes).onChange(async (value) => { this.plugin.settings.excludeFootnotes = value; await this.plugin.saveSettings(); await this.plugin.initialize(); }) ); new import_obsidian4.Setting(containerEl).setName("Character count method").setDesc("For language compatibility").addDropdown((drop) => { drop.addOption("AllCharacters" /* StringLength */, "All characters").addOption( "ExcludeWhitespace" /* ExcludeWhitespace */, "Exclude whitespace" ).setValue(this.plugin.settings.characterCountType).onChange(async (value) => { this.plugin.settings.characterCountType = value; await this.plugin.saveSettings(); await this.plugin.initialize(); }); }); new import_obsidian4.Setting(containerEl).setName("Page count method").setDesc("For language compatibility").addDropdown((drop) => { drop.addOption("ByWords" /* ByWords */, "Words per page").addOption("ByChars" /* ByChars */, "Characters per page").setValue(this.plugin.settings.pageCountType).onChange(async (value) => { this.plugin.settings.pageCountType = value; await this.plugin.saveSettings(); this.display(); await this.plugin.updateDisplayedCounts(); }); }); const wordsPerMinuteChanged = async (txt, value) => { const asNumber = Number(value); const isValid = !isNaN(asNumber) && asNumber > 0; txt.inputEl.style.borderColor = isValid ? null : "red"; this.plugin.settings.wordsPerMinute = isValid ? Number(value) : 265; await this.plugin.saveSettings(); await this.plugin.initialize(); }; new import_obsidian4.Setting(containerEl).setName("Words per minute").setDesc( "Used to calculate Reading Time. 265 is the average speed of an English-speaking adult." ).addText((txt) => { txt.setPlaceholder("265").setValue(this.plugin.settings.wordsPerMinute.toString()).onChange((0, import_obsidian4.debounce)(wordsPerMinuteChanged.bind(this, txt), 1e3)); }); const charsPerMinuteChanged = async (txt, value) => { const asNumber = Number(value); const isValid = !isNaN(asNumber) && asNumber > 0; txt.inputEl.style.borderColor = isValid ? null : "red"; this.plugin.settings.charsPerMinute = isValid ? Number(value) : 500; await this.plugin.saveSettings(); await this.plugin.initialize(); }; new import_obsidian4.Setting(containerEl).setName("CJK characters per minute").setDesc( "Used to calculate Reading Time. 500 is the average speed for CJK texts." ).addText((txt) => { txt.setPlaceholder("500").setValue(this.plugin.settings.charsPerMinute.toString()).onChange((0, import_obsidian4.debounce)(charsPerMinuteChanged.bind(this, txt), 1e3)); }); if (this.plugin.settings.pageCountType === "ByWords" /* ByWords */) { const wordsPerPageChanged = async (txt, value) => { const asNumber = Number(value); const isValid = !isNaN(asNumber) && asNumber > 0; txt.inputEl.style.borderColor = isValid ? null : "red"; this.plugin.settings.wordsPerPage = isValid ? Number(value) : 300; await this.plugin.saveSettings(); await this.plugin.initialize(); }; new import_obsidian4.Setting(containerEl).setName("Words per page").setDesc( "Used for page count. 300 is standard in English language publishing." ).addText((txt) => { txt.setPlaceholder("300").setValue(this.plugin.settings.wordsPerPage.toString()).onChange((0, import_obsidian4.debounce)(wordsPerPageChanged.bind(this, txt), 1e3)); }); } if (this.plugin.settings.pageCountType === "ByChars" /* ByChars */) { new import_obsidian4.Setting(containerEl).setName("Include whitespace characters in page count").addToggle( (toggle) => toggle.setValue(this.plugin.settings.charsPerPageIncludesWhitespace).onChange(async (value) => { this.plugin.settings.charsPerPageIncludesWhitespace = value; await this.plugin.saveSettings(); this.display(); await this.plugin.initialize(); }) ); const charsPerPageChanged = async (txt, value) => { const asNumber = Number(value); const isValid = !isNaN(asNumber) && asNumber > 0; txt.inputEl.style.borderColor = isValid ? null : "red"; const defaultCharsPerPage = 1500; this.plugin.settings.charsPerPage = isValid ? Number(value) : defaultCharsPerPage; await this.plugin.saveSettings(); await this.plugin.initialize(); }; new import_obsidian4.Setting(containerEl).setName("Characters per page").setDesc( `Used for page count. ${this.plugin.settings.charsPerPageIncludesWhitespace ? "2400 is common in Danish." : "1500 is common in German (Normseite)."}` ).addText((txt) => { txt.setPlaceholder("1500").setValue(this.plugin.settings.charsPerPage.toString()).onChange((0, import_obsidian4.debounce)(charsPerPageChanged.bind(this, txt), 1e3)); }); } const dateFormatChanged = async (txt, value) => { const isValid = typeof value === "string" && !!value.trim(); this.plugin.settings.momentDateFormat = isValid ? value : ""; await this.plugin.saveSettings(); await this.plugin.initialize(); }; new import_obsidian4.Setting(containerEl).setName("Date format").setDesc("MomentJS date format to use for date strings").addText((txt) => { txt.setPlaceholder("YYYY/MM/DD").setValue(this.plugin.settings.momentDateFormat).onChange((0, import_obsidian4.debounce)(dateFormatChanged.bind(this, txt), 1e3)); }); new import_obsidian4.Setting(containerEl).setName("Debug mode").setDesc("Log debugging information to the developer console").addToggle( (toggle) => toggle.setValue(this.plugin.settings.debugMode).onChange(async (value) => { this.plugin.settings.debugMode = value; this.plugin.debugHelper.setDebugMode(value); this.plugin.fileHelper.setDebugMode(value); await this.plugin.saveSettings(); }) ); } } renderReanalyzeButton(containerEl) { this.renderSeparator(containerEl); new import_obsidian4.Setting(containerEl).setHeading().setName("Recount all documents").setDesc( "If changes have occurred outside of Obsidian, you may need to trigger a manual recount" ).addButton( (button) => button.setButtonText("Recount").setCta().onClick(async () => { button.disabled = true; await this.plugin.initialize(); button.setButtonText("Done"); button.removeCta(); setTimeout(() => { button.setButtonText("Recount"); button.setCta(); button.disabled = false; }, 1e3); }) ); } renderDonationButton(containerEl) { this.renderSeparator(containerEl); const label = containerEl.createEl("div", { cls: [ "setting-item", "setting-item-heading", "novel-word-count-settings-header", "novel-word-count-donation-line" ] }); label.createEl("div", { text: "Enjoying this plugin? Want more features?" }); const button = label.createEl("div"); button.innerHTML = `Buy Me a Coffee at ko-fi.com`; } renderCountTypeSetting(containerEl, config) { new import_obsidian4.Setting(containerEl).setName(config.name).setDesc(getDescription(config.oldCountType)).addDropdown((drop) => { for (const countType of COUNT_TYPES) { drop.addOption(countType, COUNT_TYPE_DISPLAY_STRINGS[countType]); } drop.setValue(config.oldCountType).onChange(async (value) => { config.setNewCountType(value); await this.plugin.saveSettings(); this.display(); await this.plugin.updateDisplayedCounts(); }); }); } renderSessionCountTypeSetting(containerEl, config) { if (config.parentCountType !== "tracksession" /* TrackSession */) { return; } new import_obsidian4.Setting(containerEl).setDesc("[Track Session] Session count type").addDropdown((drop) => { for (const countType of SESSION_COUNT_TYPES) { drop.addOption(countType, COUNT_TYPE_DISPLAY_STRINGS[countType]); } drop.setValue(config.oldCountType).onChange(async (value) => { config.setNewCountType(value); await this.plugin.saveSettings(); this.display(); await this.plugin.updateDisplayedCounts(); }); }); } renderCustomFormatSetting(containerEl, config) { if (!this.plugin.settings.useAdvancedFormatting || config.countType === "none" /* None */) { return; } if (UNFORMATTABLE_COUNT_TYPES.includes(config.countType)) { new import_obsidian4.Setting(containerEl).setDesc( `[${COUNT_TYPE_DISPLAY_STRINGS[config.countType]}] can't be formatted.` ); } else { new import_obsidian4.Setting(containerEl).setDesc( `[${COUNT_TYPE_DISPLAY_STRINGS[config.countType]}] Custom suffix` ).addText( (text) => text.setValue(config.oldSuffix).onChange(async (value) => { config.setNewSuffix(value); await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }) ); } } renderFrontmatterKeySetting(containerEl, config) { if (config.countType !== "frontmatterKey" /* FrontmatterKey */) { return; } new import_obsidian4.Setting(containerEl).setDesc( `[${COUNT_TYPE_DISPLAY_STRINGS["frontmatterKey" /* FrontmatterKey */]}] Key name` ).addText( (text) => text.setValue(config.oldKey).onChange(async (value) => { config.setNewKey(value); await this.plugin.saveSettings(); await this.plugin.updateDisplayedCounts(); }) ); } renderSeparator(containerEl) { containerEl.createEl("hr", { cls: "novel-word-count-hr" }); } }; // main.ts var import_obsidian5 = require("obsidian"); var NovelWordCountPlugin = class extends import_obsidian5.Plugin { constructor(app, manifest) { super(app, manifest); this.debugHelper = new DebugHelper(); this.FIVE_MINUTES = 5 * 60 * 1e3; this.saveSettingsDebounced = (0, import_obsidian5.debounce)(this.saveSettings, this.FIVE_MINUTES, false); this.fileHelper = new FileHelper(this.app, this); this.eventHelper = new EventHelper( this, app, this.debugHelper, this.fileHelper ); this.nodeLabelHelper = new NodeLabelHelper(this); this.savedDataHelper = new SavedDataHelper(this); } get settings() { return this.savedData.settings; } // LIFECYCLE async onload() { this.savedData = await this.savedDataHelper.getSavedData(); this.fileHelper.setDebugMode(this.savedData.settings.debugMode); this.debugHelper.setDebugMode(this.savedData.settings.debugMode); this.debugHelper.debug(`Detected locales: [${navigator.languages}]`); this.debugHelper.debug("onload lifecycle hook"); this.addSettingTab(new NovelWordCountSettingTab(this.app, this)); this.addCommand({ id: "recount-vault", name: "Recount all notes / Reset session", callback: async () => { this.debugHelper.debug("[Recount] command triggered"); await this.initialize(); } }); this.addCommand({ id: "cycle-count-type", name: "Show next data type (1st position)", callback: async () => { this.debugHelper.debug("[Cycle next data type] command triggered"); this.settings.countType = COUNT_TYPES[(COUNT_TYPES.indexOf(this.settings.countType) + 1) % COUNT_TYPES.length]; await this.saveSettings(); this.updateDisplayedCounts(); } }); this.addCommand({ id: "toggle-abbreviate", name: "Toggle abbreviation on Notes", callback: async () => { this.debugHelper.debug( "[Toggle abbrevation - Notes] command triggered" ); this.settings.abbreviateDescriptions = !this.settings.abbreviateDescriptions; await this.saveSettings(); this.updateDisplayedCounts(); } }); for (const countType of COUNT_TYPES) { this.addCommand({ id: `set-count-type-${countType}`, name: `Show ${COUNT_TYPE_DISPLAY_STRINGS[countType]} (1st position)`, callback: async () => { this.debugHelper.debug( `[Set count type to ${countType}] command triggered` ); this.settings.countType = countType; await this.saveSettings(); this.updateDisplayedCounts(); } }); } this.eventHelper.handleEvents(); this.initialize(); } async onunload() { await this.saveSettings(); } // SETTINGS async saveSettings() { this.debugHelper.debug("Saving to data.json."); await this.saveData(this.savedData); } // PUBLIC /** Called with (true) when the plugin initializes or the user clicks Reanalyze. Called with (false) every second while waiting for the file explorer to load. */ async initialize(reinitializeAllCounts = true) { this.debugHelper.debug("initialize"); this.app.workspace.onLayoutReady(async () => { if (reinitializeAllCounts) { await this.eventHelper.reinitializeAllCounts(); } try { await this.getFileExplorerLeaf(); await this.updateDisplayedCounts(); } catch (err) { this.debugHelper.debug("Error while updating displayed counts"); this.debugHelper.error(err); setTimeout(() => { this.initialize(false); }, 1e3); } }); } async updateDisplayedCounts(file = null) { var _a, _b; const debugEnd = this.debugHelper.debugStart( `updateDisplayedCounts [${file == null ? "ALL" : file.path}]` ); if (!Object.keys(this.savedData.cachedCounts).length) { this.debugHelper.debug("No cached data found; skipping update."); return; } let fileExplorerLeaf; try { fileExplorerLeaf = await this.getFileExplorerLeaf(); } catch (err) { this.debugHelper.debug("File explorer leaf not found; skipping update."); return; } this.setContainerClass(fileExplorerLeaf); const fileExplorerView = fileExplorerLeaf.view; const fileItems = fileExplorerView.fileItems; if ((_a = fileExplorerView == null ? void 0 : fileExplorerView.headerDom) == null ? void 0 : _a.navButtonsEl) { const counts = this.fileHelper.getCachedDataForPath( this.savedData.cachedCounts, "/" ); fileExplorerView.headerDom.navButtonsEl.setAttribute( "data-novel-word-count-plugin", this.nodeLabelHelper.getNodeLabel(counts) ); document.documentElement.style.setProperty("--novel-word-count-opacity", `${this.settings.labelOpacity}`); } if (file) { const relevantItems = Object.keys(fileItems).filter( (path) => file.path.includes(path) ); this.debugHelper.debug( "Setting display counts for", relevantItems.length, "fileItems matching path", file.path ); } else { this.debugHelper.debug( `Setting display counts for ${Object.keys(fileItems).length} fileItems` ); } for (const path in fileItems) { if (file && (!file.path.includes(path) || file.path === "/")) { continue; } const counts = this.fileHelper.getCachedDataForPath( this.savedData.cachedCounts, path ); const item = fileItems[path]; ((_b = item.titleEl) != null ? _b : item.selfEl).setAttribute( "data-novel-word-count-plugin", this.nodeLabelHelper.getNodeLabel(counts) ); } debugEnd(); } // FUNCTIONALITY async getFileExplorerLeaf() { return new Promise((resolve, reject) => { let foundLeaf = null; this.app.workspace.iterateAllLeaves((leaf) => { if (foundLeaf) { return; } const view = leaf.view; if (!view || !view.fileItems) { return; } foundLeaf = leaf; resolve(foundLeaf); }); if (!foundLeaf) { reject(Error("Could not find file explorer leaf.")); } }); } setContainerClass(leaf) { const container = leaf.view.containerEl; container.toggleClass(`novel-word-count--active`, true); const notePrefix = `novel-word-count--note-`; const folderPrefix = `novel-word-count--folder-`; const alignmentClasses = ALIGNMENT_TYPES.map((at) => notePrefix + at).concat(ALIGNMENT_TYPES.map((at) => folderPrefix + at)); for (const ac of alignmentClasses) { container.toggleClass(ac, false); } container.toggleClass(notePrefix + this.settings.alignment, true); const folderAlignment = this.settings.showSameCountsOnFolders ? this.settings.alignment : this.settings.folderAlignment; container.toggleClass(folderPrefix + folderAlignment, true); } }; /* nosourcemap */