您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Formatters
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.gf.qytechs.cn/scripts/540511/1618540/GGn%20Formatters.js
// ==UserScript== // @name GGn Formatters // @version 3 // @description Formatters // @author ingts (some by ZeDoCaixao and letsclay) // @match https://gazellegames.net/ // ==/UserScript== function formatTitle(str, alias) { const japaneseLowercase = new Map([ ["ga", ["が", "ガ"]], ["no", ["の", "ノ"]], ["wa", ["わ", "ワ"]], ["mo", ["も", "モ"]], ["kara", ["から", "カラ"]], ["made", ["まで", "マデ"]], ["to", ["と", "ト"]], ["ya", ["や", "ヤ"]], ["de", ["で", "デ"]], ["ni", ["に", "ニ"]], ["so", ["そ", "ソ"]], ["na", ["な", "ナ"]], ["i", ["い", "イ"]], ["u", ["う", "ウ"]], ["e", ["え", "エ"]], ["o", ["お", "オ"]], ["san", ["さん"]], ["sama", ["さま"]], ["kun", ["くん"]], ["chan", ["ちゃん"]] ]) const smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|v.?|vs.?|via)$/i const alphanumericPattern = /([A-Za-z0-9\u00C0-\u00FF])/ const wordSeparators = /([ :–—-]|[^a-zA-Z0-9'’])/ const allUppercase = new Set(['rpg', 'fps', 'tps', 'rts', 'tbs', 'mmo', 'mmorpg', 'arpg', 'jrpg', 'pvp', 'pve', 'ntr', 'td', 'vr', 'npc', 'ost']) return str .replace(/\s/g, ' ').replace('—', ' - ') .replace(/~$/, '').replace(/ ~$/, '').replace(/-$/, '').replace(/^-/, '').replace(/ ~ /, ': ').replace(/ ~/, ': ').replace(/ - /, ': ').replace(/ -/, ': ') .replace('™', '').replace('®', '').replace(' : ', ': ') .toLowerCase().trim() .split(wordSeparators) .map((current, index, array) => { const isFirstOrLastWord = index !== 0 && index !== array.length - 1 if (allUppercase.has(current.trim()) || /\b([IVX])(X{0,3}I{0,3}|X{0,2}VI{0,3}|X{0,2}I?[VX])(?![A-Za-z'])\b/i.test(current)) { return current.toUpperCase() } if (alias && !isFirstOrLastWord) { const jpWords = japaneseLowercase.get(current) if (jpWords?.some(w => alias.includes(w))) return current } if ( /* Check for small words */ current.search(smallWords) > -1 && /* Skip first and last word */ isFirstOrLastWord && /* Ignore title end and subtitle start */ array[index - 3] !== ':' && array[index + 1] !== ':' && /* Ignore small words that start a hyphenated phrase */ (array[index + 1] !== '-' || (array[index - 1] === '-' && array[index + 1] === '-')) ) { return current } /* Capitalize the first letter */ return current.replace(alphanumericPattern, function (match) { return match.toUpperCase() }) }) .join('') } function formatAbout(str, gameTitle) { str = str.replace(/defence/g, 'defense') str = str.replace(/ *\/ */g, '/') // bold game title. replace [u] or [i] with bold if (gameTitle) { // noinspection RegExpSuspiciousBackref const boldTitleRegex = new RegExp(`(?:\\[([ui])\])?${gameTitle}(?:\\[\\/\\1])?`, 'i') str = str.replace(boldTitleRegex, `[b]${gameTitle}[/b]`) } // If a line starts with [u], [i], or [b], there is no other text on that line, and it contains 'features', replace tags with [align=center][b][u] str = str.replace(/^(\[b]|\[u]|\[i])*(.*?)(\[\/b]|\[\/u]|\[\/i])*$/gm, (match, p1, p2, p3) => (p1 && p3 && /features/i.test(p2)) ? `[align=center][b][u]${p2}[/u][/b][/align]` : match) // Title case text inside [align=center][b][u] str = str.replace(/\[align=center]\[([bu])]\[([bu])]([\s\S]*?)\[\/\2]\[\/\1]\[\/align]/g, (match, p1, p2, p3) => `[align=center][b][u]${formatTitle(p3)}[/u][/b][/align]`) // Add a newline before lines with [align=center] if there isn't already a double newline before it str = str.replace(/(?<!\n\n)(\[align=center])/g, '\n\$1') // Remove colons in text inside [align=center][b][u] str = str.replace(/\[align=center]\[b]\[u](.*?)\[\/u]\[\/b]\[\/align]/g, (match, p1) => match.replace(/:/g, '')) // Replace different list symbols at the start with [*] str = str.replace(/^[-•◦■・★]\s*/gm, '[*]') // If a line starts with [u], [i], or [b] and it is not the only text on that line, add [*] at the start and replace tags with [b] str = str.replace(/^(\[b]|\[u]|\[i])*(.*?)(\[\/b]|\[\/u]|\[\/i])+(.*$)/gm, (match, p1, p2, p3, p4) => { if (p4.trim() === '') { return match } return p1 && p3 ? `[*][b]${p2}[/b]${p4}` : match }) // If a line starts with [*] followed by a [u] or [i], replace them with [b] str = str.replace(/^\[\*]\[[ui]](.*?)\[\/[ui]]/gm, '[b]$1[/b]') // Title case text inside tags for lines starting with [u], [i], or [b] and has nothing else after the closing tag str = str.replace(/(^|\n)(\[([uib])](.*?)\[\/([uib])]\s*$)/gm, (match, p1, p2, p3, p4) => `${p1}[${p3}]${formatTitle(p4)}[/${p3}]`) // For lines that start with [*], replace newlines with spaces until that line ends with ., ?, or ! // and add a full stop if there is no punctuation before another [*] str = fixSplitLinesInListItems(str) // Remove double newlines between [*] lines str = str.replace(/(\[\*][^\n]*)(\n{2,})(?=\[\*])/g, '$1\n') // Add a newline when next line doesn't start with [*] str = str.replace(/(\[\*][^\n]*\n)([^\[*\]\n])/g, '$1\n$2') // Move : and . outside of closing tags str = str.replace(/(\[([bui])])(.*?)([:.])\[\/([bui])]/g, '$1\$3[/b]\$4') // Remove [u], [i], or [b] if the line starts with [*] followed by a [u], [i], or [b], and ends with punctuation after the closing tag str = str.replace(/^\[\*]\[([bui])](.*?)\[\/([bui])]([.?!。?!])$/gm, "[*]$2$4") // If a line ends with [/align] replace double newlines with one newline str = str.replace(/(\[\/align])\n\n/g, '$1\n') return str function fixSplitLinesInListItems(input) { let lines = input.split('\n') for (let i = 0; i < lines.length; i++) { if (lines[i].startsWith("[*]")) { while (i + 1 < lines.length && !lines[i].match(/[.?!。?!]$/)) { if (lines[i + 1].startsWith("[*]")) { lines[i] += '.' break } else if (lines[i + 1].trim() !== '') { lines[i] += ' ' + lines.splice(i + 1, 1)[0] } else { lines.splice(i + 1, 1) } } } } return lines.join('\n') } } function formatSysReqs(str) { if (!str) return "" str = str.replace(/:\[\/b] /g, "[/b]: ") str = str.replace(/:\n/g, "\n") str = str.replace(/:\[\/b]\n/g, "[/b]\n") str = str.replace(/\n\n\[b]/g, "\n[b]") str = str.replace(/\[\*]Requires a 64-bit processor and operating system\n(.*)/g, (match, osLine) => osLine.includes('64') ? osLine : `${osLine} (64-bit)`) str = str.replace(/OS \*/g, 'OS') str = str.replace(/(\d+)\s?(\w)b/gi, (match, p1, p2) => `${p1} ${p2.toUpperCase()}B`) str = str.replace(/([a-zA-Z]{2,})(\d)/g, '$1 $2') str = str.replace(/\(?64.?bit\)?/, "(64-bit)") str = str.replace(/ *\(?(?:or|\/) (?:better|greater|higher|over|later|equivalent)\)?/gi, '') // convert to next unit if divisible by 1024 str = str.replace(/(\d+)\s*(\w+)/gi, (match, num, unit) => { const intNum = parseInt(num) if (intNum % 1024 === 0) { return unit === 'KB' ? `${intNum / 1024} MB` : unit === 'MB' ? `${intNum / 1024} GB` : match } return match }) const osPart = getPart('OS') if (osPart) { let newPart = osPart.replace(/(?:Microsoft)?\s?Win(?:dows)?/gi, 'Windows') newPart = newPart.replace(/^\(?64-bit\)?\s?(.*)/g, "$1 (64-bit)") newPart = newPart.replace(/macos/gi, "macOS") newPart = newPart.replace(', ', '/') // Remove repeated OS names let firstSkipped = false newPart = newPart.replace(/[a-zA-Z]+ /g, match => { if (!firstSkipped) { firstSkipped = true return match } return '' }) str = str.replace(osPart, newPart) } const processorPart = getPart('Processor') if (processorPart) { let newPart = processorPart.replace(/(.*?)-core/gi, '$1 Core') newPart = newPart.replace(/(\d\.?\d?)\s?(\w)hz/gi, (match, p1, p2) => `${p1} ${p2.toUpperCase()}Hz`) newPart = newPart.replace(/(?<brand>[A-za-z]) (?<cores>.* Core) (?<hz>\d\.?\d? \w+)/, '$<cores> $<brand> $<hz>') str = str.replace(processorPart, newPart) } const graphicsPart = getPart('Graphics') if (graphicsPart) { let newPart = graphicsPart.replace(/(?:nvidia )?geforce/gi, "Nvidia GeForce") newPart = newPart.replace(/(?:of )?(?:dedicated )?(?:graphics|video)?\s?RAM|memory/gi, 'VRAM') newPart = newPart.replace(/graphics card with (.*?)\s?(?:of)? V?RAM/gi, '$1 VRAM') newPart = newPart.replace(/nvidia/gi, 'Nvidia') newPart = newPart.replace(/gpu/gi, '') newPart = newPart.replace(/(?:series|video)?\s?card?/gi, '') newPart = newPart.replace(' or ', ' / ') newPart = newPart.replace(/(\w)\/(\w)/, '$1 / $2') str = str.replace(graphicsPart, newPart) } const directxPart = getPart('DirectX') if (directxPart) { let newPart = directxPart.replace(/v(?:ersion)?\s?/i, '') str = str.replace(directxPart, newPart) } str = str.replace(/(\S)\(/, '$1 (') str = str.replace(/(\S)\+/, '$1') return str function getPart(sectionName) { return new RegExp(`\\[\\*]\\[b]${sectionName}\\[\\/b]: (.*)`).exec(str)?.[1] } } function formatCommon(str) { str = str.replace(/[™®©]/g, '') str = str.replace(/([.?!#$%&;:,])(\w)/g, '$1 $2') return str }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址