Skip to content

Commit f2f78c4

Browse files
committed
1 parent 1cc5f03 commit f2f78c4

3 files changed

Lines changed: 205 additions & 53 deletions

File tree

Lines changed: 96 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,135 @@
11
function section_screenshot_evaluate({ section, elements_to_disable }) {
2-
console.log(`Section: ${section}`); // skipcq
3-
console.log(`Elements to disable: ${elements_to_disable}`); // skipcq
2+
console.log(`Section: ${section}`) // skipcq
3+
console.log(`Elements to disable: ${elements_to_disable}`) // skipcq
44

5-
const levels = ["H1", "H2", "H3", "H4", "H5", "H6"];
6-
let sec = document.getElementById(section);
7-
while (sec) {
8-
if (levels.includes(sec.tagName)) {
9-
break;
5+
const levels = ['H1', 'H2', 'H3', 'H4', 'H5', 'H6']
6+
const headingSelector = levels.join(',').toLowerCase()
7+
8+
function findHeadingAfterAnchor(anchor) {
9+
let current = anchor
10+
while (current && current.parentElement) {
11+
if (current !== anchor && current.textContent.trim()) {
12+
return null
13+
}
14+
let sibling = current.nextElementSibling
15+
while (sibling) {
16+
if (levels.includes(sibling.tagName)) {
17+
return sibling
18+
}
19+
const nestedHeading = sibling.querySelector(headingSelector)
20+
if (nestedHeading) {
21+
return nestedHeading
22+
}
23+
if (sibling.textContent.trim() || sibling.children.length) {
24+
break
25+
}
26+
sibling = sibling.nextElementSibling
27+
}
28+
current = current.parentElement
29+
}
30+
return null
31+
}
32+
33+
function findHeadingBeforeAnchor(anchor) {
34+
let previousHeading = null
35+
for (const heading of document.querySelectorAll(headingSelector)) {
36+
const position = heading.compareDocumentPosition(anchor)
37+
if (position & Node.DOCUMENT_POSITION_FOLLOWING) {
38+
previousHeading = heading
39+
} else if (position & Node.DOCUMENT_POSITION_PRECEDING) {
40+
break
41+
}
1042
}
11-
sec = sec.parentNode;
43+
return previousHeading
44+
}
45+
46+
const target = document.getElementById(section)
47+
if (!target) {
48+
return
1249
}
13-
let sec_level = sec.tagName;
14-
console.log(sec_level);
50+
51+
let heading = target.closest(headingSelector)
52+
if (!heading && target.tagName === 'SPAN' && target.classList.contains('anchor')) {
53+
heading = findHeadingAfterAnchor(target) || findHeadingBeforeAnchor(target)
54+
}
55+
if (!heading) {
56+
return
57+
}
58+
59+
let sec = heading
60+
let sec_level = heading.tagName
61+
console.log(sec_level)
1562
if (
16-
sec.parentNode.className.includes("ext-discussiontools-init-section") ||
17-
sec.parentNode.className.includes("mw-heading")
63+
sec.parentElement.classList.contains('ext-discussiontools-init-section') ||
64+
sec.parentElement.classList.contains('mw-heading')
1865
) {
1966
// wo yi ding yao sha le ni men
20-
sec = sec.parentNode;
67+
sec = sec.parentNode
2168
}
2269

23-
let nbox = document.createElement("div");
24-
nbox.className = "bot-sectionbox";
25-
nbox.style = "display: inline-block";
26-
nbox.appendChild(sec.cloneNode(true));
70+
let nbox = document.createElement('div')
71+
nbox.className = 'bot-sectionbox'
72+
nbox.style = 'display: inline-block'
73+
nbox.appendChild(sec.cloneNode(true))
2774

28-
let next_sibling = sec.nextSibling;
75+
let next_sibling = sec.nextSibling
2976
while (next_sibling) {
3077
if (levels.includes(next_sibling.tagName)) {
31-
if (levels.indexOf(next_sibling.tagName) <= levels.indexOf(sec_level))
32-
break;
78+
if (levels.indexOf(next_sibling.tagName) <= levels.indexOf(sec_level)) break
3379
}
3480
if (
35-
next_sibling.tagName === "DIV" &&
36-
(next_sibling.className.includes("ext-discussiontools-init-section") ||
37-
next_sibling.className.includes("mw-heading"))
81+
next_sibling.tagName === 'DIV' &&
82+
(next_sibling.className.includes('ext-discussiontools-init-section') ||
83+
next_sibling.className.includes('mw-heading'))
3884
) {
39-
let child = next_sibling.firstChild;
40-
let bf = false;
85+
let child = next_sibling.firstChild
86+
let bf = false
4187
while (child) {
42-
console.log(`Child tag: ${child.tagName}`); // skipcq
88+
console.log(`Child tag: ${child.tagName}`) // skipcq
4389
if (
4490
levels.includes(child.tagName) &&
4591
levels.indexOf(child.tagName) <= levels.indexOf(sec_level)
4692
) {
47-
bf = true;
48-
break;
93+
bf = true
94+
break
4995
}
50-
child = child.nextSibling;
96+
child = child.nextSibling
5197
}
52-
if (bf) break;
98+
if (bf) break
5399
}
54-
nbox.appendChild(next_sibling.cloneNode(true));
55-
next_sibling = next_sibling.nextSibling;
100+
nbox.appendChild(next_sibling.cloneNode(true))
101+
next_sibling = next_sibling.nextSibling
56102
}
57103

58-
let lazyimg = nbox.querySelectorAll(".lazyload");
104+
let lazyimg = nbox.querySelectorAll('.lazyload')
59105
for (let i = 0; i < lazyimg.length; i++) {
60-
lazyimg[i].className = "image";
61-
let dataSrc = lazyimg[i].getAttribute("data-src");
106+
lazyimg[i].className = 'image'
107+
let dataSrc = lazyimg[i].getAttribute('data-src')
62108
if (
63-
typeof dataSrc === "string" &&
64-
(dataSrc.startsWith("http://") ||
65-
dataSrc.startsWith("https://") ||
66-
dataSrc.startsWith("/"))
109+
typeof dataSrc === 'string' &&
110+
(dataSrc.startsWith('http://') || dataSrc.startsWith('https://') || dataSrc.startsWith('/'))
67111
) {
68-
lazyimg[i].src = dataSrc;
112+
lazyimg[i].src = dataSrc
69113
} else {
70-
console.warn(`Blocked suspicious data-src value for image: ${dataSrc}`); // skipcq
114+
console.warn(`Blocked suspicious data-src value for image: ${dataSrc}`) // skipcq
71115
}
72116
}
73117

74-
let new_parentNode = sec.parentNode.cloneNode();
75-
let pparentNode = sec.parentNode.parentNode;
76-
pparentNode.removeChild(sec.parentNode);
77-
pparentNode.appendChild(new_parentNode);
78-
new_parentNode.appendChild(nbox);
118+
let new_parentNode = sec.parentNode.cloneNode()
119+
let pparentNode = sec.parentNode.parentNode
120+
pparentNode.removeChild(sec.parentNode)
121+
pparentNode.appendChild(new_parentNode)
122+
new_parentNode.appendChild(nbox)
79123

80124
for (let i = 0; i < elements_to_disable.length; i++) {
81-
let element_to_boom = document.querySelector(elements_to_disable[i]); // :rina: :rina: :rina: :rina:
125+
let element_to_boom = document.querySelector(elements_to_disable[i]) // :rina: :rina: :rina: :rina:
82126
if (element_to_boom != null) {
83-
element_to_boom.style = "display: none !important";
127+
element_to_boom.style = 'display: none !important'
84128
}
85129
}
86130

87-
document.querySelectorAll("*").forEach((element) => {
88-
element.parentNode.replaceChild(element.cloneNode(true), element);
89-
});
90-
window.scroll(0, 0);
131+
document.querySelectorAll('*').forEach((element) => {
132+
element.parentNode.replaceChild(element.cloneNode(true), element)
133+
})
134+
window.scroll(0, 0)
91135
}

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "akari-bot-webrender"
3-
version = "0.6.1"
3+
version = "0.6.2"
44
description = ""
55
authors = [
66
{name = "OasisAkari", email = "OasisAkari@gmail.com"},

tests/test_section_screenshot.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import unittest
2+
3+
from playwright.async_api import Error as PlaywrightError
4+
from playwright.async_api import async_playwright
5+
6+
from akari_bot_webrender.functions.main import section_screenshot_script
7+
8+
9+
class SectionScreenshotEvaluateTest(unittest.IsolatedAsyncioTestCase):
10+
async def asyncSetUp(self):
11+
self.playwright = await async_playwright().start()
12+
try:
13+
self.browser = await self.playwright.chromium.launch(headless=True)
14+
except PlaywrightError as exc:
15+
await self.playwright.stop()
16+
self.playwright = None
17+
self.skipTest(f"Chromium is unavailable: {exc}")
18+
self.page = await self.browser.new_page()
19+
20+
async def asyncTearDown(self):
21+
if hasattr(self, "browser"):
22+
await self.browser.close()
23+
if self.playwright:
24+
await self.playwright.stop()
25+
26+
async def evaluate_section(self, content: str, section: str):
27+
await self.page.set_content(content)
28+
await self.page.evaluate(
29+
section_screenshot_script,
30+
{"section": section, "elements_to_disable": []},
31+
)
32+
section_box = await self.page.query_selector(".bot-sectionbox")
33+
return await section_box.inner_text() if section_box else None
34+
35+
async def test_manual_anchor_before_heading_selects_following_section(self):
36+
text = await self.evaluate_section(
37+
"""
38+
<main>
39+
<div class="mw-heading"><h2 id="previous">Previous</h2></div>
40+
<p>Previous body</p>
41+
<p><span class="anchor" id="manual"></span></p>
42+
<div class="mw-heading"><h2 id="target">Target</h2></div>
43+
<p>Target body</p>
44+
<div class="mw-heading"><h2 id="next">Next</h2></div>
45+
<p>Next body</p>
46+
</main>
47+
""",
48+
"manual",
49+
)
50+
51+
self.assertIn("Target", text)
52+
self.assertIn("Target body", text)
53+
self.assertNotIn("Previous body", text)
54+
self.assertNotIn("Next", text)
55+
56+
async def test_manual_anchor_inside_heading_selects_that_section(self):
57+
text = await self.evaluate_section(
58+
"""
59+
<main>
60+
<h2><span class="anchor" id="manual"></span>Target</h2>
61+
<p>Target body</p>
62+
<h2>Next</h2>
63+
<p>Next body</p>
64+
</main>
65+
""",
66+
"manual",
67+
)
68+
69+
self.assertIn("Target", text)
70+
self.assertIn("Target body", text)
71+
self.assertNotIn("Next", text)
72+
73+
async def test_manual_anchor_in_section_body_selects_containing_section(self):
74+
text = await self.evaluate_section(
75+
"""
76+
<main>
77+
<h2 id="target">Target</h2>
78+
<p>Before anchor</p>
79+
<p><span class="anchor" id="manual"></span>After anchor</p>
80+
<h2 id="next">Next</h2>
81+
<p>Next body</p>
82+
</main>
83+
""",
84+
"manual",
85+
)
86+
87+
self.assertIn("Target", text)
88+
self.assertIn("Before anchor", text)
89+
self.assertIn("After anchor", text)
90+
self.assertNotIn("Next", text)
91+
92+
async def test_non_anchor_span_does_not_select_a_section(self):
93+
text = await self.evaluate_section(
94+
"""
95+
<main>
96+
<span class="not-anchor" id="manual"></span>
97+
<h2>Target</h2>
98+
<p>Target body</p>
99+
</main>
100+
""",
101+
"manual",
102+
)
103+
104+
self.assertIsNone(text)
105+
106+
107+
if __name__ == "__main__":
108+
unittest.main()

0 commit comments

Comments
 (0)