Version 0.1.2
This commit is contained in:
parent
cb8c201b95
commit
d32b0243d8
5 changed files with 114 additions and 25 deletions
|
@ -11,7 +11,7 @@ import commonjs from '@rollup/plugin-commonjs';
|
|||
import replace from '@rollup/plugin-replace';
|
||||
import postcss from 'rollup-plugin-postcss';
|
||||
import alias from 'rollup-plugin-alias';
|
||||
import _dotenv from 'dotenv/config';
|
||||
// import _dotenv from 'dotenv/config';
|
||||
import path from "path";
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,8 +1,69 @@
|
|||
import browser from 'webextension-polyfill';
|
||||
|
||||
let awaitingResponse = false
|
||||
|
||||
const onFeedDownloaded = (req: XMLHttpRequest) => {
|
||||
return async () => {
|
||||
awaitingResponse = false
|
||||
if (req.status >= 400) {
|
||||
console.error(
|
||||
`Could not load URL feed: ${req.responseURL}: ` +
|
||||
`${req.status}: ${req.statusText}`
|
||||
)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const [tab] = await browser.tabs.query({
|
||||
active: true,
|
||||
currentWindow: true
|
||||
})
|
||||
|
||||
if (!tab || tab.id === -1)
|
||||
return
|
||||
|
||||
await browser.tabs.sendMessage(
|
||||
tab.id, {
|
||||
type: 'renderFeed',
|
||||
document: req.responseText
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const renderFeed = (url: string) => {
|
||||
awaitingResponse = true
|
||||
const req = new XMLHttpRequest()
|
||||
req.onload = onFeedDownloaded(req)
|
||||
req.open('GET', url)
|
||||
req.responseType = 'text'
|
||||
req.send()
|
||||
}
|
||||
|
||||
browser.webNavigation.onCompleted.addListener(
|
||||
async (event: {tabId: string;}) => {
|
||||
async (event: {tabId: Number}) => {
|
||||
const { tabId } = event
|
||||
await browser.tabs.sendMessage(tabId, {type: 'renderFeed'})
|
||||
}
|
||||
)
|
||||
|
||||
browser.webRequest.onHeadersReceived.addListener(
|
||||
async (event: {
|
||||
url: string,
|
||||
responseHeaders: Array<{name: string, value: string}>
|
||||
}) => {
|
||||
if (awaitingResponse)
|
||||
return
|
||||
|
||||
const {url, responseHeaders} = event
|
||||
const contentType = responseHeaders.find(
|
||||
h => h.name.toLowerCase() === 'content-type'
|
||||
)?.value || ''
|
||||
|
||||
if (contentType.startsWith('application/rss+xml'))
|
||||
renderFeed(url)
|
||||
},
|
||||
{urls: ['<all_urls>']},
|
||||
['blocking', 'responseHeaders']
|
||||
)
|
||||
|
||||
|
|
53
src/main.ts
53
src/main.ts
|
@ -103,9 +103,9 @@ const parseFeed = (channel: Element) => {
|
|||
}
|
||||
}
|
||||
|
||||
const getFeedRoot = () => {
|
||||
const getFeedRoot = (): HTMLElement | null => {
|
||||
const xmlDoc = document.documentElement
|
||||
if (xmlDoc.tagName === 'rss')
|
||||
if (xmlDoc.tagName.toLowerCase() === 'rss')
|
||||
return xmlDoc
|
||||
|
||||
// Chrome-based browsers may wrap the XML into an HTML view
|
||||
|
@ -116,28 +116,31 @@ const getFeedRoot = () => {
|
|||
// For some ugly reasons, some RSS feeds are rendered inside of a <pre> in a normal HTML DOM
|
||||
const preElements = document.getElementsByTagName('pre')
|
||||
if (preElements.length !== 1)
|
||||
return
|
||||
return null
|
||||
|
||||
const text = preElements[0].innerText
|
||||
return preElements[0]
|
||||
}
|
||||
|
||||
const textToDOM = (text: string) => {
|
||||
const parser = new DOMParser()
|
||||
let innerXmlDoc = null
|
||||
let xmlDoc = null
|
||||
|
||||
try {
|
||||
// @ts-ignore
|
||||
innerXmlDoc = parser.parseFromString(text, 'text/xml')
|
||||
xmlDoc = parser.parseFromString(text, 'text/xml')
|
||||
} catch (e) { }
|
||||
|
||||
if (!innerXmlDoc)
|
||||
if (!xmlDoc)
|
||||
return
|
||||
|
||||
// @ts-ignore
|
||||
const root = innerXmlDoc.documentElement
|
||||
if (root.tagName === 'rss')
|
||||
const root = xmlDoc.documentElement
|
||||
if (root.tagName.toLowerCase() === 'rss')
|
||||
return root
|
||||
}
|
||||
|
||||
const renderFeed = () => {
|
||||
const xmlDoc = getFeedRoot()
|
||||
const renderFeed = (text: string) => {
|
||||
const xmlDoc = text?.length ? textToDOM(text) : getFeedRoot()
|
||||
if (!xmlDoc)
|
||||
// Not an RSS feed
|
||||
return
|
||||
|
@ -153,9 +156,9 @@ const renderFeed = () => {
|
|||
const extractFeedUrl = () => {
|
||||
const links = Array.from(document.getElementsByTagName('link'))
|
||||
.filter((link) =>
|
||||
link.getAttribute('rel') === 'alternate' &&
|
||||
link.getAttribute('type') === 'application/rss+xml'
|
||||
)
|
||||
link.getAttribute('rel') === 'alternate' &&
|
||||
link.getAttribute('type')?.startsWith('application/rss+xml')
|
||||
)
|
||||
|
||||
if (!links.length)
|
||||
return
|
||||
|
@ -171,10 +174,20 @@ const extractFeedUrl = () => {
|
|||
return link.length ? link : null
|
||||
}
|
||||
|
||||
browser.runtime.onMessage.addListener(async (message: {type: Object}) => {
|
||||
if (message.type === 'renderFeed')
|
||||
return renderFeed()
|
||||
if (message.type === 'extractFeedUrl')
|
||||
return extractFeedUrl()
|
||||
})
|
||||
browser.runtime.onMessage.addListener(
|
||||
async (
|
||||
message: {
|
||||
type: Object,
|
||||
url: string,
|
||||
document: string,
|
||||
}
|
||||
) => {
|
||||
if (message.type === 'renderFeed')
|
||||
return renderFeed(message.document)
|
||||
if (message.type === 'extractFeedUrl')
|
||||
return extractFeedUrl()
|
||||
|
||||
console.warn(`Received unknown message type: ${message.type}`)
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "RSS Viewer",
|
||||
"description": "An easy way to render RSS feeds directly in your browser",
|
||||
"version": "0.1.1",
|
||||
"version": "0.1.2",
|
||||
"manifest_version": 2,
|
||||
"browser_action": {
|
||||
"default_title": "Feed Viewer",
|
||||
|
@ -26,6 +26,11 @@
|
|||
"viewer/index.html"
|
||||
],
|
||||
"permissions": [
|
||||
"activeTab", "storage", "<all_urls>", "webNavigation"
|
||||
"activeTab",
|
||||
"storage",
|
||||
"<all_urls>",
|
||||
"webNavigation",
|
||||
"webRequest",
|
||||
"webRequestBlocking"
|
||||
]
|
||||
}
|
||||
|
|
|
@ -64,6 +64,10 @@ export default {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.feed {
|
||||
font-family: -apple-system, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Open Sans", "Droid Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
header {
|
||||
padding: 0.5em;
|
||||
box-shadow: 1px 1px 1px 1px #b7b7b7;
|
||||
|
@ -153,9 +157,15 @@ main {
|
|||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
font-size: 1.25em;
|
||||
font-family: sans-serif;
|
||||
|
||||
.description {
|
||||
max-width: 45em;
|
||||
text-align: justify;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue