// ==UserScript== // @name OceanEngine Document Maker // @namespace http://tampermonkey.net/ // @version 1.0 // @description Translate document to enumeration and type defintion // @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 = (row) => { const tds = row.querySelectorAll('td') 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 parseRows = (rows) => { if (!rows.length) return let content = `data class Foo (\n` rows.forEach(row => { content += parseRow(row) }) content += ')' return content } const parseTable = (table, selector, result) => { result.innerText = parseRows(table.querySelectorAll(selector)) 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 }) })();