Forráskód Böngészése

添加 'adq-doc-table.user.js'

吴迪 3 éve
szülő
commit
de99c61de8
1 módosított fájl, 151 hozzáadás és 0 törlés
  1. 151 0
      adq-doc-table.user.js

+ 151 - 0
adq-doc-table.user.js

@@ -0,0 +1,151 @@
+// ==UserScript==
+// @name         GDT Table Transformer Kotlin
+// @namespace    http://tampermonkey.net/
+// @version      1.0
+// @description  try to take over the world!
+// @author       You
+// @match        https://developers.e.qq.com/docs/*
+// @grant        GM_setClipboard
+// @run-at       document-start
+// ==/UserScript==
+
+
+(function() {
+    'use strict';
+    const makeClassName = s => s?.replace(/(?:_|^)([a-z])/g, g => g.substring(g.length - 1).toUpperCase()) || ''
+    const convertType = (type, name) => {
+        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 'int64':
+            case 'long':
+            case 'integer': type = 'Long'; break
+            case 'boolean': type = 'Boolean'; break
+            case 'string': type = 'String'; break
+            case 'enum': type = makeClassName(name) || 'Enum'; break
+            case 'struct':
+            case 'object': type = makeClassName(name) || 'Object'; break
+        }
+        if (isList) type = `List<${type}>`
+        return type
+    }
+    const parseName = (td) => {
+        if (!td) return
+        for (const node of td.querySelector('span.flex').childNodes) {
+            if (node.nodeType === Node.TEXT_NODE) {
+                const text = node.textContent.trim()
+                if (text) return text.replace(/\n/g, '')
+            }
+        }
+    }
+    const parseRow = (tds) => {
+        const name = parseName(tds[0])
+        if (!name) return ''
+        const optional = !!tds[0].querySelector('span.must')
+        const type = tds[1].innerText.trim()
+        const comment = tds[2].innerText.trim()
+        return `  val ${name}: ${convertType(type, name)}${optional ? '? = null' : ''}, // ${comment.replace(/\n/g, ' ')}\n`
+    }
+    const parseEnumRow = (tds) => {
+        const name = parseName(tds[0])
+        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, level, parentLevel, result) => {
+        const getRows = (lv) => table.querySelectorAll(`tbody > tr:has(td.${lv})`)
+        const checkIsEnum = () => {
+            const headers = table.querySelectorAll('thead td')
+            if (headers.length !== 2) return false
+            return headers[0].innerText?.trim() === '名称'
+        }
+        const isEnum = checkIsEnum()
+        const getOuterTitle = () => {
+            let title = 'Root'
+            if (isEnum) {
+                title = table.closest('div.colunm')?.querySelector('.colunm-tit')?.innerText
+            } else {
+                let node = table.closest('div.doc-intr').previousElementSibling
+                while (node) {
+                    if (/^H\d$/.test(node.tagName)) {
+                        title = node.innerText.replace(/\s/g, '')
+                        break
+                    }
+                    node = node.previousElementSibling
+                }
+            }
+            return title
+        }
+        const boundaries = parentLevel ? Array.from(getRows(parentLevel)).map(node => [makeClassName(parseName(node.querySelector('td'))), node]) : [[getOuterTitle(), null]] // [title as string, boundary as Node]
+        const closingTag = isEnum ? '}' : ')'
+        let content = ''
+        const fields = []
+        getRows(level).forEach(row => {
+            const prevNode = row.previousSibling
+            const boundary = boundaries.find(b => prevNode === b[1])
+            if (boundary) {
+                if (content) {
+                    // not first entity, close current item
+                    content += closingTag + '\n'
+                }
+                const title = boundary[0]
+                content += isEnum ? `enum class ${title} {\n` : `data class ${title} (\n`
+            }
+            const name = parseName(row.querySelector('td')) // first column is name
+            if (name) fields.push(name)
+            content += (isEnum ? parseEnumRow : parseRow)(row.querySelectorAll('td'))
+        })
+        if (content) content += closingTag + '\n'
+        GM_setClipboard(content)
+        // For circumstance that need field name list
+        content += fields.map(v => `'${v}'`).join(isEnum ? ' | ' : ', ') + '\n'
+        result.innerText = content
+        result.style.display = ''
+    }
+
+    const createButton = (table) => {
+        if (table.dataset.done) return
+        table.dataset.done = true
+        if (!table.querySelector('thead')) return // not interested
+        const levels = Array.from(table.querySelectorAll('tr>td:first-child')).reduce((s, row) => {
+            if (row.className === '') {
+                row.className = 'level-one'
+            } else if (row.className?.startsWith('level-')) {
+                s.add(row.className)
+            }
+            return s
+        }, new Set(['level-one']))
+        const container = document.createElement('div')
+        const result = document.createElement('pre')
+        result.style.display = 'none'
+        const parent = table.parentNode
+        parent.insertBefore(container, table)
+        parent.insertBefore(result, table)
+        const addBtn = (level, parentLevel) => {
+            const btn = document.createElement('button')
+            btn.append(document.createTextNode(level))
+            btn.addEventListener('click', () => parseTable(table, level, parentLevel, result))
+            container.append(btn)
+        }
+        Array.from(levels.values()).forEach((lv, i, a) => addBtn(lv, a[i - 1]))
+    }
+
+    document.addEventListener('DOMContentLoaded', () => {
+        const ob = new MutationObserver(mutations => {
+            mutations.forEach(({ target }) => {
+                if (target?.tagName === 'ARTICLE') {
+                    console.log(target)
+                    target.querySelectorAll('table').forEach(createButton)
+                }
+            })
+        })
+        ob.observe(document.body, { subtree: true, childList: true })
+        setTimeout(() => document.querySelectorAll('table').forEach(createButton), 3000)
+    })
+})();