// ==UserScript== // @name OceanEngine Table Transformer // @namespace http://tampermonkey.net/ // @version 1.0 // @description try to take over the world! // @author You // @match https://open.oceanengine.com/labels/7/docs/* // @grant GM_setClipboard // @run-at document-start // ==/UserScript== (function() { 'use strict'; const convertType = (type) => { const isList = type.endsWith('[]') if (isList) type = type.substring(0, type.length - 2) switch (type.toLowerCase()) { case 'float': type = 'Float'; break case 'double': type = 'Double'; break case 'number': case 'int': case 'long': case 'integer': type = 'Long'; break case 'boolean': type = 'Boolean'; break case 'string': type = 'String'; break case 'json': case 'object': type = 'Object'; break } if (isList) type = `List<${type}>` return type } const parseName = (childNodes) => { if (!childNodes) return for (const node of childNodes) { if (node.nodeType === Node.TEXT_NODE) { const text = node.textContent.trim() if (text) return text } } } const parseRow = (tds) => { const name = parseName(tds[0].querySelector('div')?.childNodes) if (!name) return '' const optional = tds[0].querySelector('span.tag')?.innerText?.trim() !== '必填' const type = tds[1].innerText.trim() const comment = tds[2].innerText.trim() return ` val ${name.replace(/\n/g, '')}: ${convertType(type)}${optional ? '? = null' : ''}, // ${comment.replace(/\n/g, ' ')}\n` } const parseEnumRow = (tds) => { const name = parseName(tds[0].querySelector('div')?.childNodes) if (!name) return '' let safeName = name.toUpperCase().replace(/\n/g, '').replace(/[^A-Z0-9_]/g, '_') if (!/^[A-Z]/.test(safeName)) safeName = `_${safeName}` const comment = tds[1].innerText.trim() return `${name === safeName ? '' : ` @SerializedName("${name}")\n`} ${safeName}, // ${comment.replace(/\n/g, ' ')}\n` } const parseTable = (table, selector, result) => { let node = table.previousElementSibling let title while (node) { if (/^H\d$/.test(node.tagName)) { title = node.innerText.replace(/\s/g, '') break } node = node.previousElementSibling } const isEnum = table.querySelector('th')?.innerText?.trim() === '值' let content = isEnum ? `enum class ${title} {\n` : `data class ${title} (\n` table.querySelectorAll(selector).forEach(row => { content += (isEnum ? parseEnumRow : parseRow)(row.querySelectorAll('td')) }) content += isEnum ? '}' : ')' result.innerText = content result.style.display = '' GM_setClipboard(result.innerText) } const ENUM_HINT = '允许值' const processEnumerationNode = (node) => { if (node.nodeType === Node.ELEMENT_NODE && node.tagName === 'CODE') { if (node.previousSibling?.tagName !== 'BR') node.parentNode.insertBefore(document.createElement('br'), node) node.innerText = node.innerText.replace(/"|'/g, '') } else if (node.nodeType === Node.TEXT_NODE && node.previousSibling?.tagName === 'CODE') { const comment = node.textContent.trim().replace(/、|,|,$/g, '') node.textContent = comment ? `,\u00A0//\u00A0${comment}` : ',' } } const formatEnumerations = (table) => { table.querySelectorAll('tr > td:nth-child(3) > div').forEach(div => { if (div.innerText?.includes(ENUM_HINT)) { // 提示模式 允许值:blahblah let started = false let node = div.childNodes[0] while (node) { if (started) { processEnumerationNode(node) } else if (node.nodeType === Node.TEXT_NODE && node.textContent.includes(ENUM_HINT)) { started = true } node = node.nextSibling } } else { // 行首模式 let node = div.querySelector('br + code') if (node?.previousSibling?.tagName !== 'BR') return while (node) { processEnumerationNode(node) node = node.nextSibling } } }) } const createButton = (table) => { if (table.dataset.done) return table.dataset.done = true formatEnumerations(table) const levels = Array.from(table.querySelectorAll('tr')).reduce((s, row) => { if (row.className) s.add(row.className) return s }, new Set()) const container = document.createElement('div') const result = document.createElement('pre') result.style.display = 'none' table.parentNode.insertBefore(container, table) table.parentNode.insertBefore(result, table) const addBtn = (level) => { const btn = document.createElement('button') btn.append(document.createTextNode(level)) btn.addEventListener('click', () => parseTable(table, `tbody > tr.${level}`, result)) container.append(btn) } for (const level of levels.values()) addBtn(level) } const ob = new MutationObserver(mutations => { mutations.forEach(({ target }) => { if (target?.tagName === 'DIV' && target?.classList?.contains('doc-content-body')) { target.querySelectorAll('table').forEach(createButton) } }) }) ob.observe(document.body, { subtree: true, childList: true }) setTimeout(() => document.querySelectorAll('table').forEach(createButton), 3000) })();