83 lines
3.2 KiB
Text
83 lines
3.2 KiB
Text
# select_match.ludic - whether a selector picks a node, and how specific it is (as CSS counts)
|
|
function sel_spec(s: UiSel) -> int {
|
|
var n = 0
|
|
var at = s
|
|
while at != null {
|
|
if at.id != "" { n = n + 100 }
|
|
n = n + (len(at.classes) + len(at.akeys) + len(at.pseudos)) * 10
|
|
if at.tag != "" { n = n + 1 }
|
|
for i in 0 .. len(at.nots) { n = n + sel_spec(at.nots[i]) }
|
|
at = at.prev
|
|
}
|
|
return n
|
|
}
|
|
function sel_match(ui_st: mut UiState, s: UiSel, n: UiNode) -> bool {
|
|
if not sel_one(ui_st, s, n) { return false }
|
|
if s.prev == null { return true }
|
|
if s.comb == 1 { return n.parent != null and sel_match(ui_st, s.prev, n.parent) }
|
|
if s.comb == 2 { return n.parent != null and n.index > 0 and sel_match(ui_st, s.prev, n.parent.children[n.index - 1]) }
|
|
if s.comb == 3 {
|
|
if n.parent == null { return false }
|
|
for i in 0 .. n.index {
|
|
if sel_match(ui_st, s.prev, n.parent.children[i]) { return true }
|
|
}
|
|
return false
|
|
}
|
|
var a = n.parent
|
|
while a != null {
|
|
if sel_match(ui_st, s.prev, a) { return true }
|
|
a = a.parent
|
|
}
|
|
return false
|
|
}
|
|
function sel_one(ui_st: mut UiState, s: UiSel, n: UiNode) -> bool {
|
|
if s.tag != "" and s.tag != n.tag { return false }
|
|
if s.id != "" and s.id != n.id { return false }
|
|
for i in 0 .. len(s.classes) {
|
|
if not sel_has(n.classes, s.classes[i]) { return false }
|
|
}
|
|
for i in 0 .. len(s.akeys) {
|
|
let v = sel_attr_of(n, s.akeys[i])
|
|
if v == null or (s.aops[i] == 1 and v != s.avals[i]) { return false }
|
|
}
|
|
for i in 0 .. len(s.pseudos) {
|
|
if not sel_pseudo_ok(ui_st, s.pseudos[i], s.pargs[i], n) { return false }
|
|
}
|
|
for i in 0 .. len(s.nots) {
|
|
if sel_one(ui_st, s.nots[i], n) { return false }
|
|
}
|
|
return true
|
|
}
|
|
function sel_has(xs: []string, x: string) -> bool {
|
|
if xs == null { return false }
|
|
for i in 0 .. len(xs) {
|
|
if xs[i] == x { return true }
|
|
}
|
|
return false
|
|
}
|
|
function sel_attr_of(n: UiNode, key: string) -> string {
|
|
if n.akeys == null { return null }
|
|
for i in 0 .. len(n.akeys) {
|
|
if n.akeys[i] == key { return vl_text(n.vals[i]) }
|
|
}
|
|
return null
|
|
}
|
|
function sel_pseudo_ok(ui_st: mut UiState, p: string, arg: string, n: UiNode) -> bool {
|
|
if p == "hover" { return n.hovered }
|
|
if p == "focus" { return ui_st.fc_key != "" and n.key == ui_st.fc_key }
|
|
if p == "focus-visible" { return ui_st.fc_visible and ui_st.fc_key != "" and n.key == ui_st.fc_key }
|
|
if p == "focus-within" { return ui_st.fc_key != "" and lc_mine(ui_st.fc_key, n.key) }
|
|
if p == "disabled" { return not n.enabled }
|
|
if p == "enabled" { return n.enabled }
|
|
if p == "first-child" { return n.index == 0 }
|
|
if p == "last-child" { return n.parent != null and n.index == len(n.parent.children) - 1 }
|
|
if p == "only-child" { return n.parent != null and len(n.parent.children) == 1 }
|
|
if p == "empty" { return len(n.children) == 0 and n.text == "" }
|
|
if p == "root" { return n.parent == null }
|
|
if p == "checked" { return ui_attr_on(n, "checked") }
|
|
if p == "active" { return fr_active(ui_st, n) }
|
|
if p == "capturing" { return ui_st.ct_capture != "" and n.key == ui_st.ct_capture }
|
|
if p == "nth-child" { return sel_nth(arg, n.index + 1) }
|
|
ui_err(ui_st, `:{p}: there is no such pseudo-class here`)
|
|
return false
|
|
}
|