Mercurial > addons > firefox-addons > feed-preview
diff js/feed-preview.js @ 6:5d7c13e998e9
Create feed previews using a stream filter
Instead of replacing the feed document with an XHTML preview from a content
script after it has already been rendered, create an XHTML preview using a
stream filter before it is passed into the rendering engine and use an XSL
style sheet to convert it to HTML. This has two advantages, firstly it
results in an HTMLDocument with the full HTML DOM available and secondly it
avoids rendering the document twice.
Refactor the feed preview creation and split parsing and rendering into
seperate modules.
author | Guido Berhoerster <guido+feed-preview@berhoerster.name> |
---|---|
date | Thu, 08 Nov 2018 16:30:34 +0100 |
parents | |
children | fcd65cf3f634 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/js/feed-preview.js Thu Nov 08 16:30:34 2018 +0100 @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2018 Guido Berhoerster <guido+feed-preview@berhoerster.name> + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +'use strict'; + +export function renderFeedPreview(feedPreviewDocument, feed) { + // inject XSL stylesheet which transforms XHTML to HTML allowing the use of + // the HTML DOM + let xslFilename = browser.runtime.getURL('web_resources/xhtml-to-html.xsl'); + let xmlStylesheetNode = + feedPreviewDocument.createProcessingInstruction('xml-stylesheet', + `type="application/xslt+xml" href="${xslFilename}"`); + feedPreviewDocument.firstChild.after(xmlStylesheetNode); + + feedPreviewDocument.querySelector('link[rel=stylesheet]').href = + browser.runtime.getURL('web_resources/style/feed-preview.css'); + + feedPreviewDocument.querySelector('title').textContent = feed.title; + feedPreviewDocument.querySelector('#feed-title').textContent = feed.title; + feedPreviewDocument.querySelector('#feed-subtitle').textContent = + feed.subtitle; + + if (typeof feed.logo !== 'undefined') { + let feedLogoTemplate = + feedPreviewDocument.querySelector('#feed-logo-template'); + let logoNode = feedPreviewDocument.importNode(feedLogoTemplate.content, + true); + let imgElement = logoNode.querySelector('#feed-logo'); + imgElement.setAttribute('src', feed.logo.url); + imgElement.setAttribute('alt', feed.logo.title); + feedPreviewDocument.querySelector('#feed-header').prepend(logoNode); + } + + let entryTemplateElement = + feedPreviewDocument.querySelector('#entry-template'); + let entryTitleTemplateElement = + feedPreviewDocument.querySelector('#entry-title-template'); + let entryTitleLinkedTemplateElement = + feedPreviewDocument.querySelector('#entry-title-linked-template'); + let entryFileListTemplateElement = + feedPreviewDocument.querySelector('#entry-files-list-template'); + let entryFileTemplateElement = + feedPreviewDocument.querySelector('#entry-file-template'); + if (feed.entries.length === 0) { + let hintTemplateElement = + previewDocument.querySelector('#no-entries-hint-template'); + let hintNode = previewDocument.importNode(hintTemplateElement.content, + true); + hintNode.querySelector("#no-entries-hint").textContent = + browser.i18n.getMessage('noEntriesHint'); + + previewDocument.body.append(hintNode); + } + for (let entry of feed.entries) { + let entryNode = + feedPreviewDocument.importNode(entryTemplateElement.content, + true); + let titleElement; + let titleNode; + + if (typeof entry.link !== 'undefined') { + titleNode = feedPreviewDocument + .importNode(entryTitleLinkedTemplateElement.content, true); + titleElement = titleNode.querySelector('.entry-link'); + titleElement.href = entry.link; + titleElement.title = entry.title; + } else { + titleNode = feedPreviewDocument + .importNode(entryTitleTemplateElement.content, true); + titleElement = titleNode.querySelector('.entry-title'); + } + titleElement.textContent = entry.title; + entryNode.querySelector('.entry-header').prepend(titleNode); + + let timeElement = entryNode.querySelector('.entry-date > time'); + timeElement.textContent = entry.date.toLocaleString(); + + let contentElement = entryNode.querySelector('.entry-content'); + let contentDocument = new DOMParser().parseFromString(entry.content, + 'text/html'); + let stylesheetElement = contentDocument.createElement('link'); + stylesheetElement.rel = 'stylesheet'; + stylesheetElement.href = + browser.runtime.getURL('web_resources/style/entry-content.css'); + contentDocument.head.appendChild(stylesheetElement); + contentElement.srcdoc = new XMLSerializer() + .serializeToString(contentDocument); + contentElement.title = entry.title; + + if (entry.files.length > 0) { + let fileListNode = feedPreviewDocument + .importNode(entryFileListTemplateElement.content, true); + fileListNode.querySelector('.entry-files-title').textContent = + browser.i18n.getMessage('filesTitle'); + let fileListElement = + fileListNode.querySelector('.entry-files-list'); + + for (let file of entry.files) { + let fileNode = feedPreviewDocument + .importNode(entryFileTemplateElement.content, true); + + let fileLinkElement = + fileNode.querySelector('.entry-file-link'); + fileLinkElement.href = file.url; + fileLinkElement.title = file.filename; + fileLinkElement.textContent = file.filename; + + fileNode.querySelector('.entry-file-info').textContent = + `(${file.type}, ${file.size} bytes)`; + + fileListElement.appendChild(fileNode); + } + + entryNode.querySelector('.entry').append(fileListNode); + } + + feedPreviewDocument.body.append(entryNode); + } +}