Skip to content

Commit 7d115b3

Browse files
committed
1 parent 743e920 commit 7d115b3

File tree

10 files changed

+4850
-2
lines changed

10 files changed

+4850
-2
lines changed

.eslintrc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"env": {
3+
"browser": true,
4+
"es6": true,
5+
"amd": true,
6+
"webextensions": true
7+
}
8+
}

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,8 @@ typings/
5959

6060
# next.js build output
6161
.next
62+
63+
# Build output
64+
/addon/**/*.js
65+
web-ext-artifacts/
66+
.web-extension-id

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,17 @@
1-
# aoc-to-markdown
2-
Generates Markdown for a given Advent of Code page.
1+
# Advent of Code to Markdown
2+
Converts a given [Advent of Code](https://adventofcode.com) page to a
3+
GitHub-compatible Markdown file.
4+
5+
## Building
6+
7+
- `npm install`
8+
- `npm run build`
9+
10+
The extension root is the [addon](addon/) folder which can be packaged/tested
11+
as appropriate.
12+
13+
## Testing
14+
15+
- `npm install --global web-ext`
16+
- `cd addon/`
17+
- `web-ext run`

addon/icons/aoc-to-markdown.svg

Lines changed: 1 addition & 0 deletions
Loading

addon/manifest.json

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
{
2+
"manifest_version": 2,
3+
"name": "Advent of Code to Markdown",
4+
"version": "1.0.2",
5+
"description": "Converts a given Advent of Code page to a GitHub-compatible Markdown file.",
6+
"icons": {
7+
"32": "icons/aoc-to-markdown.svg"
8+
},
9+
"page_action": {
10+
"default_icon": "icons/aoc-to-markdown.svg",
11+
"default_title": "Advent of Code to Markdown"
12+
},
13+
"background": {
14+
"scripts": ["background_scripts/index.js"]
15+
},
16+
"permissions": [
17+
"activeTab",
18+
"tabs",
19+
"downloads"
20+
],
21+
"applications": {
22+
"gecko": {
23+
"id": "{9255ec71-5fba-48c8-9d40-0ae6ef3d6fb3}"
24+
}
25+
}
26+
}

background_scripts/background.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
function urlIsApplicable(url) {
2+
const regex = /^https:\/\/adventofcode.com\/\d{4}\/day\/\d{1,2}/;
3+
return regex.test(url);
4+
}
5+
6+
function initializePageAction(tab) {
7+
if (urlIsApplicable(tab.url)) {
8+
browser.pageAction.show(tab.id);
9+
} else {
10+
browser.pageAction.hide(tab.id);
11+
}
12+
}
13+
14+
browser.tabs.onUpdated.addListener((id, changeInfo, tab) => {
15+
initializePageAction(tab);
16+
});
17+
18+
browser.runtime.onMessage.addListener((message) => {
19+
if (message.action === "saveAs") {
20+
const blob = new Blob([message.text], { type: "text/plain;charset=utf-8" });
21+
22+
browser.downloads.download({
23+
url: URL.createObjectURL(blob),
24+
filename: "README.md",
25+
saveAs: true,
26+
}).catch((e) => {
27+
console.error(e);
28+
});
29+
} else {
30+
console.warn(`Unknown action: ${message.action}`);
31+
}
32+
});
33+
34+
browser.pageAction.onClicked.addListener(() => {
35+
browser.tabs.executeScript({file: "/content_scripts/index.js"})
36+
.catch((e) => {
37+
console.error(e);
38+
});
39+
});
40+
41+
browser.tabs.query({}).then((tabs) => {
42+
for (const tab of tabs) {
43+
initializePageAction(tab);
44+
}
45+
});

content_scripts/to-markdown.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
const gfm = require('turndown-plugin-gfm').gfm;
2+
const TurndownService = require('turndown').default;
3+
4+
function stripHyphens (str) {
5+
const regex = /^--- (.+) ---$/;
6+
return str.replace(regex, (match, p1) => {
7+
return p1;
8+
});
9+
}
10+
11+
const newDoc = document.createDocumentFragment();
12+
const titleElement = document.createElement('h1');
13+
newDoc.appendChild(titleElement);
14+
15+
const link = document.createElement('a');
16+
link.href = window.location.href;
17+
link.innerText = window.location.href;
18+
newDoc.appendChild(link);
19+
20+
const descriptionHeader = document.createElement('h2');
21+
descriptionHeader.innerText = 'Description';
22+
newDoc.appendChild(descriptionHeader);
23+
24+
const articleElements = document.querySelectorAll('article');
25+
const articleElementsLength = articleElements.length;
26+
for (let index = 0; index < articleElementsLength; ++index) {
27+
const article = articleElements[index].cloneNode(true);
28+
const headingElement = article.querySelector('h2');
29+
let heading = stripHyphens(headingElement.innerText);
30+
31+
if (index === 0) {
32+
titleElement.innerText = heading;
33+
heading = 'Part One';
34+
}
35+
36+
const newHeadingElement = document.createElement('h3');
37+
newHeadingElement.innerText = heading;
38+
headingElement.replaceWith(newHeadingElement);
39+
40+
newDoc.appendChild(article);
41+
}
42+
43+
const turndownService = new TurndownService({
44+
headingStyle: 'atx'
45+
});
46+
turndownService.use(gfm);
47+
48+
const markdown = turndownService.turndown(newDoc).concat('\n');
49+
50+
browser.runtime.sendMessage({
51+
action: "saveAs",
52+
text: markdown,
53+
});

0 commit comments

Comments
 (0)