给CSS适配人员用的辅助工具函数
当前为
此脚本不应直接安装。它是供其他脚本使用的外部库,要使用该库请加入元指令 // @require https://update.cn-greasyfork.org/scripts/439632/1015906/CSS%E6%94%B9%E7%BC%96%E7%BD%91%E9%A1%B5%E5%93%8D%E5%BA%94%E5%BC%8F%20%E9%80%82%E9%85%8D%E5%B7%A5%E4%BD%9C%E8%80%85.js
// ==UserScript==
// @name CSS改编/网页响应式 适配工作者
// @version 0.55
// @description 给CSS适配人员用的辅助工具函数
// @description:en Helper functions for adaptation developers
// @match *://*/*
// @license The Unlicense
// @namespace https://greasyfork.org/users/871942
// ==/UserScript==
function extract_those_elements_whose_attributes_include_styles(doc = document, filterAttr) {
const elems = []
elems.push(...[...doc.all].filter(el => (!filterAttr || el.hasAttribute(filterAttr)) && !/\b(a|img|span)\b/.test(el.localName) && (el.localName === 'iframe' ? elems.push(...extract_those_elements_whose_attributes_include_styles(el.contentDocument)) && false : el.attributes.style?.value)))
return elems
}
function extractStyleToCssForm(elem) {
let { localName, attributes: { id = '', class: className = '', style } } = elem
if (specTags.has(localName)) {
id = className = ''
} else {
localName = ''
if (className) className = className.value.replace(/ |^/g, '.')
if (id) {
id = /[-]|auto|\bid\b/.test(id.value) ? '' : `#${id.value}`
if (id) className = ''
}
}
return `${localName}${id}${className} { ${style.value.replace(/(:) /g, '$1').replace(/;\b/g, '$& ').replace(/;$/, '')} }`
}
const specTags = new Set(['body'])
unsafeWindow._extractCssOfModifiedInPage =
function extractCssOfModifiedInPage(doc, { filterAttr = '', existing = 'user-custom' } = {}) {
doc = doc.getRootNode() // `doc` can be an arbitrarily selected leaf node without having to pay attention to selecting `HTMLDocument`
let orig = existing && doc instanceof Node && doc.querySelector(`style[${existing}]`)?.innerText || ''
, new_ = extract_those_elements_whose_attributes_include_styles(doc, filterAttr).map(extractStyleToCssForm).join('\n')
return { modified: mergeCommonCss(new_), merged: mergeCommonCss(orig + new_) }
}
function mergeCommonCss(css = '') {
const re = {
node: [/^(\s*)([^{}}]+)\s*\{([^}]+?)\s*\}(.*?)\2\{([^}]+?)\s*\}/ms, '$1$2{$3;$5 }$4'],
nodes: [/([^{\n]+?)(\s*\{[^}]+\})(.*?)\s*([^{\n]+?)\2/s, '$1, $4$2$3']
}
let merged
Object.values(re).forEach(([mat, pla]) => {
const merge = str => str.replace(mat, pla)
merged = merge(css)
while (css !== merged) merged = merge(css = merged)
})
return merged
}
unsafeWindow._findOverflowedElems =
function findOverflowedElems(rootElem) {
if (!(rootElem instanceof HTMLElement)) throw TypeError('An entry element is required to be specified.')
return [...rootElem.querySelectorAll('*')].filter(el => el.clientWidth > rootElem.clientWidth)
}