view js/feed-preview.js @ 37:6bd8a649186d

Fix charset encoding in rewritten Content-Type header The charset encoding in the rewritten Content-Type header must be UTF-8 since that is what the original response body will be converted to. This fixes the garbled text seen when viewing feeds using charset encodings other than UTF-8.
author Guido Berhoerster <guido+feed-preview@berhoerster.name>
date Mon, 01 Apr 2019 21:04:05 +0200
parents 4492db3b277e
children
line wrap: on
line source

/*
 * 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';

function formatFileSize(size) {
    const LIMITS_UNITS = new Map([
        [1024 * 1024 * 1024, 'GiB'],
        [1024 * 1024, 'MiB'],
        [1024, 'KiB'],
        [0, 'B']
    ])
    for (let [limit, unit] of LIMITS_UNITS) {
        if (size >= limit) {
            return `${Number(size / limit).toFixed(1)} ${unit}`;
        }
    }
    return '? B';
}

export function renderFeedPreview(feedPreviewDocument, feed,
        expandEntriesByDefault) {
    // 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('#default-stylesheet').href =
            browser.runtime.getURL('web_resources/style/feed-preview.css');

    // link to the currently viewed feed in order to allow feed reader addons
    // to subscribe to it
    let feedLinkElement = feedPreviewDocument.querySelector('#feed-link');
    feedLinkElement.href = feed.url;
    feedLinkElement.type = feed.type;

    feedPreviewDocument.querySelector('title').textContent = feed.title;

    feedPreviewDocument.querySelector('label[for="feed-reader-selection"]')
            .textContent = browser.i18n.getMessage('feedReaderSelectionLabel');
    feedPreviewDocument.querySelector('[name="subscribe"]').textContent =
            browser.i18n.getMessage('subscribeButtonLabel');

    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);
    }

    feedPreviewDocument.querySelector("#no-entries-hint").textContent =
            browser.i18n.getMessage('noEntriesHint');

    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');
    for (let entry of feed.entries) {
        let entryNode =
                feedPreviewDocument.importNode(entryTemplateElement.content,
                true);
        entryNode.querySelector('details.entry').open = expandEntriesByDefault;

        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);
        // open links in a new tab rather than within the iframe
        for (let linkElement of contentDocument.links) {
            linkElement.target = '_blank';
        }
        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}, ${ formatFileSize(file.size)})`;

                fileListElement.appendChild(fileNode);
            }

            entryNode.querySelector('.entry').append(fileListNode);
        }

        feedPreviewDocument.body.append(entryNode);
    }
}