135 lines
4.5 KiB
Python
135 lines
4.5 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""Batch fix for 课评汇总 class pages:
|
|
1) Remove the 评星 (star rating) feature: strip each <div class="metrics">…</div> block,
|
|
delete star/metric CSS rules, and remove the dead star JS functions.
|
|
2) Fix the 翻页卡死 (pagination freeze) on carousel pages: switch slides from
|
|
absolute-stacked opacity to display:none for inactive cards (only the active
|
|
card is ever laid out/painted), and remove backdrop-filter blur (compositing cost).
|
|
|
|
Only applied to the first-week pages when <target> is the folder.
|
|
"""
|
|
import os, re, sys
|
|
|
|
# ------------- balanced <div class="metrics"> removal -------------
|
|
def remove_metrics(html):
|
|
marker = '<div class="metrics">'
|
|
res = []
|
|
i = 0
|
|
n = len(html)
|
|
while True:
|
|
idx = html.find(marker, i)
|
|
if idx == -1:
|
|
res.append(html[i:])
|
|
break
|
|
res.append(html[i:idx])
|
|
depth = 1
|
|
j = idx + len(marker)
|
|
while depth > 0 and j < n:
|
|
oi = html.find('<div', j)
|
|
ci = html.find('</div>', j)
|
|
if ci == -1:
|
|
j = n
|
|
break
|
|
if oi != -1 and oi < ci:
|
|
depth += 1
|
|
j = oi + 4
|
|
else:
|
|
depth -= 1
|
|
j = ci + 6
|
|
i = j
|
|
return ''.join(res)
|
|
|
|
# ------------- balanced CSS block parser -------------
|
|
def strip_css(css, is_carousel):
|
|
out = []
|
|
i = 0
|
|
n = len(css)
|
|
while i < n:
|
|
m = re.match(r'\s+', css[i:])
|
|
if m:
|
|
out.append(m.group(0)); i += m.end(); continue
|
|
m = re.match(r'/\*.*?\*/', css[i:], re.S)
|
|
if m:
|
|
out.append(m.group(0)); i += m.end(); continue
|
|
brace = css.find('{', i)
|
|
if brace == -1:
|
|
out.append(css[i:]); break
|
|
sel = css[i:brace]
|
|
depth = 1
|
|
j = brace + 1
|
|
while depth > 0 and j < n:
|
|
if css[j] == '{': depth += 1
|
|
elif css[j] == '}': depth -= 1
|
|
j += 1
|
|
body = css[brace+1:j-1]
|
|
full = css[i:j]
|
|
selnorm = sel.strip()
|
|
if selnorm.startswith('@media') or selnorm.startswith('@supports'):
|
|
out.append(selnorm + '{' + strip_css(body, is_carousel) + '}')
|
|
elif re.search(r'\.(?:metric|star)', selnorm):
|
|
pass # drop star / metric rules
|
|
elif is_carousel and re.match(r'^\.student-card', selnorm):
|
|
if '.prev' in selnorm:
|
|
pass # drop the now-unneeded prev-state rule
|
|
elif '.active' in selnorm:
|
|
out.append('.student-card.active { display: block; position: relative; pointer-events: auto; }')
|
|
elif re.match(r'^\.student-card\s*$', selnorm):
|
|
out.append('.student-card { display: none; }')
|
|
else:
|
|
out.append(full) # .student-card:hover
|
|
else:
|
|
out.append(full)
|
|
i = j
|
|
return ''.join(out)
|
|
|
|
def process_styles(html, is_carousel):
|
|
def repl(m):
|
|
css = strip_css(m.group(2), is_carousel)
|
|
css = re.sub(r'backdrop-filter\s*:\s*blur\([^)]*\);?', '', css)
|
|
return m.group(1) + css + m.group(3)
|
|
return re.sub(r'(<style[^>]*>)(.*?)(</style>)', repl, html, flags=re.S)
|
|
|
|
# ------------- dead star JS removal -------------
|
|
def remove_function(js, name):
|
|
pat = re.compile(r'function\s+' + re.escape(name) + r'\s*\(')
|
|
m = pat.search(js)
|
|
if not m:
|
|
return js
|
|
brace = js.find('{', m.end())
|
|
if brace == -1:
|
|
return js
|
|
depth = 1
|
|
j = brace + 1
|
|
n = len(js)
|
|
while depth > 0 and j < n:
|
|
if js[j] == '{': depth += 1
|
|
elif js[j] == '}': depth -= 1
|
|
j += 1
|
|
return js[:m.start()] + js[j:]
|
|
|
|
def remove_call(js, name):
|
|
return re.sub(r'^[ \t]*' + re.escape(name) + r'\(\).*?$', '', js, flags=re.M)
|
|
|
|
def fix(html):
|
|
is_carousel = 'id="cardsSlider"' in html
|
|
html = remove_metrics(html)
|
|
html = process_styles(html, is_carousel)
|
|
if 'function initStarRatings' in html and 'initStarRatings' in html:
|
|
html = remove_call(html, 'initStarRatings')
|
|
html = remove_function(html, 'initStarRatings')
|
|
html = remove_function(html, 'setStarRating')
|
|
return html
|
|
|
|
def main(files):
|
|
for path in files:
|
|
with open(path, encoding='utf-8') as f:
|
|
html = f.read()
|
|
out = fix(html)
|
|
changed = out != html
|
|
with open(path, 'w', encoding='utf-8') as f:
|
|
f.write(out)
|
|
print(('CHANGED ' if changed else 'same ') + os.path.basename(path))
|
|
|
|
if __name__ == '__main__':
|
|
main(sys.argv[1:])
|