comparison 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
comparison
equal deleted inserted replaced
5:341a0f4b7ce0 6:5d7c13e998e9
1 /*
2 * Copyright (C) 2018 Guido Berhoerster <guido+feed-preview@berhoerster.name>
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 */
8
9 'use strict';
10
11 export function renderFeedPreview(feedPreviewDocument, feed) {
12 // inject XSL stylesheet which transforms XHTML to HTML allowing the use of
13 // the HTML DOM
14 let xslFilename = browser.runtime.getURL('web_resources/xhtml-to-html.xsl');
15 let xmlStylesheetNode =
16 feedPreviewDocument.createProcessingInstruction('xml-stylesheet',
17 `type="application/xslt+xml" href="${xslFilename}"`);
18 feedPreviewDocument.firstChild.after(xmlStylesheetNode);
19
20 feedPreviewDocument.querySelector('link[rel=stylesheet]').href =
21 browser.runtime.getURL('web_resources/style/feed-preview.css');
22
23 feedPreviewDocument.querySelector('title').textContent = feed.title;
24 feedPreviewDocument.querySelector('#feed-title').textContent = feed.title;
25 feedPreviewDocument.querySelector('#feed-subtitle').textContent =
26 feed.subtitle;
27
28 if (typeof feed.logo !== 'undefined') {
29 let feedLogoTemplate =
30 feedPreviewDocument.querySelector('#feed-logo-template');
31 let logoNode = feedPreviewDocument.importNode(feedLogoTemplate.content,
32 true);
33 let imgElement = logoNode.querySelector('#feed-logo');
34 imgElement.setAttribute('src', feed.logo.url);
35 imgElement.setAttribute('alt', feed.logo.title);
36 feedPreviewDocument.querySelector('#feed-header').prepend(logoNode);
37 }
38
39 let entryTemplateElement =
40 feedPreviewDocument.querySelector('#entry-template');
41 let entryTitleTemplateElement =
42 feedPreviewDocument.querySelector('#entry-title-template');
43 let entryTitleLinkedTemplateElement =
44 feedPreviewDocument.querySelector('#entry-title-linked-template');
45 let entryFileListTemplateElement =
46 feedPreviewDocument.querySelector('#entry-files-list-template');
47 let entryFileTemplateElement =
48 feedPreviewDocument.querySelector('#entry-file-template');
49 if (feed.entries.length === 0) {
50 let hintTemplateElement =
51 previewDocument.querySelector('#no-entries-hint-template');
52 let hintNode = previewDocument.importNode(hintTemplateElement.content,
53 true);
54 hintNode.querySelector("#no-entries-hint").textContent =
55 browser.i18n.getMessage('noEntriesHint');
56
57 previewDocument.body.append(hintNode);
58 }
59 for (let entry of feed.entries) {
60 let entryNode =
61 feedPreviewDocument.importNode(entryTemplateElement.content,
62 true);
63 let titleElement;
64 let titleNode;
65
66 if (typeof entry.link !== 'undefined') {
67 titleNode = feedPreviewDocument
68 .importNode(entryTitleLinkedTemplateElement.content, true);
69 titleElement = titleNode.querySelector('.entry-link');
70 titleElement.href = entry.link;
71 titleElement.title = entry.title;
72 } else {
73 titleNode = feedPreviewDocument
74 .importNode(entryTitleTemplateElement.content, true);
75 titleElement = titleNode.querySelector('.entry-title');
76 }
77 titleElement.textContent = entry.title;
78 entryNode.querySelector('.entry-header').prepend(titleNode);
79
80 let timeElement = entryNode.querySelector('.entry-date > time');
81 timeElement.textContent = entry.date.toLocaleString();
82
83 let contentElement = entryNode.querySelector('.entry-content');
84 let contentDocument = new DOMParser().parseFromString(entry.content,
85 'text/html');
86 let stylesheetElement = contentDocument.createElement('link');
87 stylesheetElement.rel = 'stylesheet';
88 stylesheetElement.href =
89 browser.runtime.getURL('web_resources/style/entry-content.css');
90 contentDocument.head.appendChild(stylesheetElement);
91 contentElement.srcdoc = new XMLSerializer()
92 .serializeToString(contentDocument);
93 contentElement.title = entry.title;
94
95 if (entry.files.length > 0) {
96 let fileListNode = feedPreviewDocument
97 .importNode(entryFileListTemplateElement.content, true);
98 fileListNode.querySelector('.entry-files-title').textContent =
99 browser.i18n.getMessage('filesTitle');
100 let fileListElement =
101 fileListNode.querySelector('.entry-files-list');
102
103 for (let file of entry.files) {
104 let fileNode = feedPreviewDocument
105 .importNode(entryFileTemplateElement.content, true);
106
107 let fileLinkElement =
108 fileNode.querySelector('.entry-file-link');
109 fileLinkElement.href = file.url;
110 fileLinkElement.title = file.filename;
111 fileLinkElement.textContent = file.filename;
112
113 fileNode.querySelector('.entry-file-info').textContent =
114 `(${file.type}, ${file.size} bytes)`;
115
116 fileListElement.appendChild(fileNode);
117 }
118
119 entryNode.querySelector('.entry').append(fileListNode);
120 }
121
122 feedPreviewDocument.body.append(entryNode);
123 }
124 }