313 lines
8.5 KiB
JavaScript
313 lines
8.5 KiB
JavaScript
/** Minimal CSS: tag / .class / #id / * plus a few properties. */
|
|
|
|
var BARE_SUMMON_UA_CSS = [
|
|
{ sel: 'a', decl: { color: 'blue', 'text-decoration': 'underline' } },
|
|
{ sel: 'b', decl: { 'font-weight': 'bold' } },
|
|
{ sel: 'strong', decl: { 'font-weight': 'bold' } },
|
|
{ sel: 'em', decl: { 'font-style': 'italic' } },
|
|
{ sel: 'i', decl: { 'font-style': 'italic' } },
|
|
{
|
|
sel: 'h1',
|
|
decl: {
|
|
'font-weight': 'bold',
|
|
'margin-top': '1em',
|
|
'margin-bottom': '0.5em'
|
|
}
|
|
},
|
|
{ sel: 'h2', decl: { 'font-weight': 'bold', 'margin-top': '1em' } },
|
|
{ sel: 'h3', decl: { 'font-weight': 'bold' } },
|
|
{ sel: 'pre', decl: { 'white-space': 'pre', 'font-family': 'monospace' } },
|
|
{ sel: 'code', decl: { 'font-family': 'monospace' } },
|
|
{ sel: 'script', decl: { display: 'none' } },
|
|
{ sel: 'style', decl: { display: 'none' } },
|
|
{ sel: 'head', decl: { display: 'none' } },
|
|
{ sel: 'title', decl: { display: 'none' } },
|
|
{ sel: 'meta', decl: { display: 'none' } },
|
|
{ sel: 'link', decl: { display: 'none' } },
|
|
{ sel: 'noscript', decl: { display: 'none' } }
|
|
]
|
|
|
|
function bareSummonParseDeclarations(block) {
|
|
var decl = {}
|
|
var parts = String(block || '').split(';')
|
|
for (var i = 0; i < parts.length; i++) {
|
|
var p = parts[i]
|
|
var c = p.indexOf(':')
|
|
if (c < 0) continue
|
|
var k = p.slice(0, c).trim().toLowerCase()
|
|
var v = p.slice(c + 1).trim()
|
|
if (k) decl[k] = v
|
|
}
|
|
return decl
|
|
}
|
|
|
|
function bareSummonStripAtRules(css) {
|
|
var s = String(css || '')
|
|
var out = ''
|
|
var i = 0
|
|
while (i < s.length) {
|
|
if (s.charAt(i) === '@') {
|
|
var brace = s.indexOf('{', i)
|
|
if (brace < 0) break
|
|
var depth = 0
|
|
var j = brace
|
|
for (; j < s.length; j++) {
|
|
if (s.charAt(j) === '{') depth++
|
|
else if (s.charAt(j) === '}') {
|
|
depth--
|
|
if (depth === 0) {
|
|
j++
|
|
break
|
|
}
|
|
}
|
|
}
|
|
i = j
|
|
continue
|
|
}
|
|
out += s.charAt(i)
|
|
i++
|
|
}
|
|
return out
|
|
}
|
|
|
|
function bareSummonParseStylesheet(css, maxRules) {
|
|
var s = bareSummonStripAtRules(
|
|
String(css || '').replace(/\/\*[\s\S]*?\*\//g, '')
|
|
)
|
|
var rules = []
|
|
var cap = maxRules > 0 ? maxRules : 800
|
|
var re = /([^{}]+)\{([^{}]*)\}/g
|
|
var m
|
|
while ((m = re.exec(s)) && rules.length < cap) {
|
|
var sels = m[1].split(',')
|
|
var decl = bareSummonParseDeclarations(m[2])
|
|
for (var i = 0; i < sels.length; i++) {
|
|
var sel = sels[i].trim()
|
|
if (sel) rules.push({ sel: sel, decl: decl })
|
|
}
|
|
}
|
|
return rules
|
|
}
|
|
|
|
function bareSummonHasClass(node, name) {
|
|
var cls = (node.attrs && node.attrs.class ? node.attrs.class : '').split(
|
|
/\s+/
|
|
)
|
|
return cls.indexOf(name) >= 0
|
|
}
|
|
|
|
function bareSummonMatchCompound(sel, node) {
|
|
if (!node || node.type !== 'element') return false
|
|
sel = String(sel || '').trim()
|
|
if (!sel || sel === '*') return true
|
|
if (sel.charAt(0) === ':') return false
|
|
var i = 0
|
|
var tag = ''
|
|
if (/^[a-zA-Z]/.test(sel)) {
|
|
while (i < sel.length && /[a-zA-Z0-9_-]/.test(sel.charAt(i))) i++
|
|
tag = sel.slice(0, i).toLowerCase()
|
|
if (node.name !== tag) return false
|
|
}
|
|
while (i < sel.length) {
|
|
var ch = sel.charAt(i)
|
|
if (ch === '.') {
|
|
i++
|
|
var c0 = i
|
|
while (i < sel.length && /[a-zA-Z0-9_-]/.test(sel.charAt(i))) i++
|
|
if (!bareSummonHasClass(node, sel.slice(c0, i))) return false
|
|
} else if (ch === '#') {
|
|
i++
|
|
var i0 = i
|
|
while (i < sel.length && /[a-zA-Z0-9_-]/.test(sel.charAt(i))) i++
|
|
if ((node.attrs.id || '') !== sel.slice(i0, i)) return false
|
|
} else if (ch === '[') {
|
|
var end = sel.indexOf(']', i)
|
|
if (end < 0) return false
|
|
var inside = sel.slice(i + 1, end)
|
|
var eq = inside.indexOf('=')
|
|
var an = (eq < 0 ? inside : inside.slice(0, eq)).trim().toLowerCase()
|
|
var av =
|
|
eq < 0
|
|
? null
|
|
: inside
|
|
.slice(eq + 1)
|
|
.trim()
|
|
.replace(/^["']|["']$/g, '')
|
|
var have = node.attrs ? node.attrs[an] : undefined
|
|
if (eq < 0) {
|
|
if (have == null) return false
|
|
} else if (String(have) !== av) return false
|
|
i = end + 1
|
|
} else if (ch === ':') {
|
|
i++
|
|
while (i < sel.length && /[a-zA-Z0-9_-]/.test(sel.charAt(i))) i++
|
|
if (sel.charAt(i) === '(') {
|
|
var depth = 1
|
|
i++
|
|
while (i < sel.length && depth) {
|
|
if (sel.charAt(i) === '(') depth++
|
|
else if (sel.charAt(i) === ')') depth--
|
|
i++
|
|
}
|
|
}
|
|
} else return false
|
|
}
|
|
return true
|
|
}
|
|
|
|
function bareSummonSplitCombinators(sel) {
|
|
var parts = []
|
|
var cur = ''
|
|
var comb = ' '
|
|
var i
|
|
var s = String(sel || '')
|
|
.replace(/\s+/g, ' ')
|
|
.trim()
|
|
for (i = 0; i < s.length; i++) {
|
|
var ch = s.charAt(i)
|
|
if (ch === '>' || ch === '+' || ch === '~') {
|
|
if (cur.trim()) parts.push({ sel: cur.trim(), comb: comb })
|
|
cur = ''
|
|
comb = ch
|
|
if (s.charAt(i + 1) === ' ') i++
|
|
} else if (ch === ' ') {
|
|
if (cur.trim()) {
|
|
parts.push({ sel: cur.trim(), comb: comb })
|
|
cur = ''
|
|
comb = ' '
|
|
}
|
|
} else cur += ch
|
|
}
|
|
if (cur.trim()) parts.push({ sel: cur.trim(), comb: comb })
|
|
return parts
|
|
}
|
|
|
|
function bareSummonPrevElement(node) {
|
|
if (!node || !node.parent) return null
|
|
var kids = node.parent.children || []
|
|
var i
|
|
var prev = null
|
|
for (i = 0; i < kids.length; i++) {
|
|
if (kids[i] === node) return prev
|
|
if (kids[i] && kids[i].type === 'element') prev = kids[i]
|
|
}
|
|
return prev
|
|
}
|
|
|
|
function bareSummonMatches(sel, node) {
|
|
if (!node || node.type !== 'element') return false
|
|
sel = String(sel || '').trim()
|
|
if (!sel) return false
|
|
var groups = sel.split(',')
|
|
var g
|
|
for (g = 0; g < groups.length; g++) {
|
|
if (bareSummonMatchesOne(groups[g].trim(), node)) return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
function bareSummonMatchesOne(sel, node) {
|
|
var parts = bareSummonSplitCombinators(sel)
|
|
if (!parts.length) return false
|
|
var cur = node
|
|
var p = parts.length - 1
|
|
if (!bareSummonMatchCompound(parts[p].sel, cur)) return false
|
|
p--
|
|
while (p >= 0 && cur) {
|
|
var comb = parts[p + 1] ? parts[p + 1].comb : ' '
|
|
if (comb === '>') {
|
|
cur = cur.parent
|
|
if (!cur || !bareSummonMatchCompound(parts[p].sel, cur)) return false
|
|
p--
|
|
} else if (comb === '+') {
|
|
cur = bareSummonPrevElement(cur)
|
|
if (!cur || !bareSummonMatchCompound(parts[p].sel, cur)) return false
|
|
p--
|
|
} else if (comb === '~') {
|
|
var ok = false
|
|
var sib = bareSummonPrevElement(cur)
|
|
while (sib) {
|
|
if (bareSummonMatchCompound(parts[p].sel, sib)) {
|
|
ok = true
|
|
cur = sib
|
|
break
|
|
}
|
|
sib = bareSummonPrevElement(sib)
|
|
}
|
|
if (!ok) return false
|
|
p--
|
|
} else {
|
|
var hit = false
|
|
var anc = cur.parent
|
|
while (anc) {
|
|
if (bareSummonMatchCompound(parts[p].sel, anc)) {
|
|
hit = true
|
|
cur = anc
|
|
break
|
|
}
|
|
anc = anc.parent
|
|
}
|
|
if (!hit) return false
|
|
p--
|
|
}
|
|
}
|
|
return p < 0
|
|
}
|
|
|
|
function bareSummonMatchSimple(sel, node) {
|
|
return bareSummonMatches(sel, node)
|
|
}
|
|
|
|
function bareSummonQueryAll(root, sel, cap) {
|
|
var out = []
|
|
var max = cap > 0 ? cap : 2000
|
|
function walk(n) {
|
|
if (!n || out.length >= max) return
|
|
if (n.type === 'element' && bareSummonMatches(sel, n)) out.push(n)
|
|
var ch = n.children || []
|
|
var i
|
|
for (i = 0; i < ch.length; i++) walk(ch[i])
|
|
}
|
|
walk(root)
|
|
return out
|
|
}
|
|
|
|
function bareSummonComputedStyle(node, sheets) {
|
|
var out = {}
|
|
function apply(decl) {
|
|
var k
|
|
for (k in decl) {
|
|
if (Object.prototype.hasOwnProperty.call(decl, k)) out[k] = decl[k]
|
|
}
|
|
}
|
|
var i
|
|
for (i = 0; i < BARE_SUMMON_UA_CSS.length; i++) {
|
|
if (bareSummonMatches(BARE_SUMMON_UA_CSS[i].sel, node))
|
|
apply(BARE_SUMMON_UA_CSS[i].decl)
|
|
}
|
|
if (sheets) {
|
|
for (i = 0; i < sheets.length; i++) {
|
|
var rules = sheets[i]
|
|
for (var r = 0; r < rules.length; r++) {
|
|
if (bareSummonMatches(rules[r].sel, node)) apply(rules[r].decl)
|
|
}
|
|
}
|
|
}
|
|
if (node && node.attrs && node.attrs.style) {
|
|
apply(bareSummonParseDeclarations(node.attrs.style))
|
|
}
|
|
return out
|
|
}
|
|
|
|
function bareSummonDisplay(style) {
|
|
var d = style && style.display ? String(style.display).toLowerCase() : ''
|
|
if (d === 'none') return 'none'
|
|
if (style && String(style.visibility || '').toLowerCase() === 'hidden')
|
|
return 'none'
|
|
if (d === 'inline' || d === 'inline-block' || d === 'inline-flex')
|
|
return 'inline'
|
|
if (d === 'flex' || d === 'grid' || d === 'table' || d === 'list-item')
|
|
return 'block'
|
|
return ''
|
|
}
|