tripledoc
Version:
Library to read, create and update documents on a Solid Pod
493 lines (397 loc) • 11.1 kB
JavaScript
import xml from 'xml'
const GENERATOR = 'Feed for Node.js'
const DOCTYPE = '<?xml version="1.0" encoding="utf-8"?>\n'
class Feed {
constructor(options) {
this.options = options
this.items = []
this.categories = []
this.contributors = []
this.extensions = []
}
addItem(item) {
this.items.push(item)
}
addCategory(category) {
this.categories.push(category)
}
addContributor(contributor) {
this.contributors.push(contributor)
}
addExtension(extension) {
this.extensions.push(extension)
}
render(format) {
console.warn('DEPRECATED: use atom1() or rss2() instead of render()');
if (format === 'atom-1.0') {
return this.atom1();
} else {
return this.rss2();
}
}
atom1() {
const { options } = this
let feed = [
{ _attr: { xmlns: 'http://www.w3.org/2005/Atom' } },
{ id: options.id },
{ title: options.title },
{ updated: (options.updated ? this.ISODateString(options.updated) : this.ISODateString(new Date())) },
{ generator: options.generator || GENERATOR },
]
let root = [{ feed }]
if (options.author) {
const { name, email, link } = options.author
let author = []
if (name) {
author.push({ name });
}
if (email) {
author.push({ email });
}
if (link) {
author.push({ uri: link });
}
feed.push({ author })
}
// link (rel="alternate")
if(options.link) {
feed.push({ link: { _attr: { rel: 'alternate', href: options.link }}});
}
// link (rel="self")
const atomLink = options.feed || (options.feedLinks && options.feedLinks.atom);
if(atomLink) {
feed.push({ "link": { _attr: { rel: 'self', href: atomLink }}});
}
// link (rel="hub")
if(options.hub) {
feed.push({ link: { _attr: { rel:'hub', href: options.hub }}});
}
/**************************************************************************
* "feed" node: optional elements
*************************************************************************/
if(options.description) {
feed.push({ subtitle: options.description });
}
if(options.image) {
feed.push({ logo: options.image });
}
if(options.favicon) {
feed.push({ icon: options.favicon });
}
if(options.copyright) {
feed.push({ rights: options.copyright });
}
this.categories.forEach(category => {
feed.push({ category: [{ _attr: { term: category } }] });
})
this.contributors.forEach(item => {
const { name, email, link } = item
let contributor = [];
if(name) {
contributor.push({ name });
}
if(email) {
contributor.push({ email });
}
if(link) {
contributor.push({ uri: link });
}
feed.push({ contributor });
})
// icon
/**************************************************************************
* "entry" nodes
*************************************************************************/
this.items.forEach(item => {
//
// entry: required elements
//
let entry = [
{ title: { _attr: { type: 'html' }, _cdata: item.title }},
{ id: item.id || item.link },
{ link: [{ _attr: { href: item.link } }]},
{ updated: this.ISODateString(item.date) }
]
//
// entry: recommended elements
//
if(item.description) {
entry.push({ summary: { _attr: { type: 'html' }, _cdata: item.description }});
}
if(item.content) {
entry.push({ content: { _attr: { type: 'html' }, _cdata: item.content }});
}
// entry author(s)
if(Array.isArray(item.author)) {
item.author.forEach(oneAuthor => {
const { name, email, link } = oneAuthor
let author = [];
if(name) {
author.push({ name });
}
if(email) {
author.push({ email });
}
if(link) {
author.push({ uri: link });
}
entry.push({ author });
})
}
// content
// link - relative link to article
//
// entry: optional elements
//
// category
// contributor
if(Array.isArray(item.contributor)) {
item.contributor.forEach(item => {
const { name, email, link } = item
let contributor = [];
if(name) {
contributor.push({ name });
}
if(email) {
contributor.push({ email });
}
if(link) {
contributor.push({ uri: link });
}
entry.push({ contributor });
})
}
// published
if(item.published) {
entry.push({ published: this.ISODateString(item.published) });
}
// source
// rights
if(item.copyright) {
entry.push({ rights: item.copyright });
}
feed.push({ entry: entry });
})
return DOCTYPE + xml(root, true);
}
rss2() {
const { options } = this
let isAtom = false
let isContent = false
let channel = [
{ title: options.title },
{ link: options.link },
{ description: options.description },
{ lastBuildDate: (options.updated ? options.updated.toUTCString() : new Date().toUTCString()) },
{ docs: 'http://blogs.law.harvard.edu/tech/rss'},
{ generator: options.generator || GENERATOR },
]
let rss = [
{ _attr: { version: '2.0' } },
{ channel },
]
let root = [{ rss }]
/**
* Channel Image
* http://cyber.law.harvard.edu/rss/rss.html#ltimagegtSubelementOfLtchannelgt
*/
if(options.image) {
channel.push({
image: [
{ title: options.title },
{ url: options.image },
{ link: options.link },
]
});
}
/**
* Channel Copyright
* http://cyber.law.harvard.edu/rss/rss.html#optionalChannelElements
*/
if(options.copyright) {
channel.push({ copyright: options.copyright });
}
/**
* Channel Categories
* http://cyber.law.harvard.edu/rss/rss.html#comments
*/
this.categories.forEach(category => {
channel.push({ category });
})
/**
* Feed URL
* http://validator.w3.org/feed/docs/warning/MissingAtomSelfLink.html
*/
const atomLink = options.feed || (options.feedLinks && options.feedLinks.atom);
if(atomLink) {
isAtom = true
channel.push({
"atom:link": {
_attr: {
href: atomLink,
rel: 'self',
type: 'application/rss+xml',
},
},
})
}
/**
* Hub for PubSubHubbub
* https://code.google.com/p/pubsubhubbub/
*/
if(options.hub) {
isAtom = true;
channel.push({
"atom:link": {
_attr: {
href: options.hub,
rel: 'hub',
},
},
})
}
/**
* Channel Categories
* http://cyber.law.harvard.edu/rss/rss.html#hrelementsOfLtitemgt
*/
this.items.forEach(entry => {
let item = [];
if(entry.title) {
item.push({ title: { _cdata: entry.title }});
}
if(entry.link) {
item.push({ link: entry.link });
}
if(entry.guid) {
item.push({ guid: entry.guid });
} else if (entry.link) {
item.push({ guid: entry.link });
}
if(entry.date) {
item.push({ pubDate: entry.date.toUTCString() });
}
if(entry.description) {
item.push({ description: { _cdata: entry.description }});
}
if(entry.content) {
isContent = true;
item.push({ 'content:encoded': { _cdata: entry.content }});
}
/**
* Item Author
* http://cyber.law.harvard.edu/rss/rss.html#ltauthorgtSubelementOfLtitemgt
*/
if(Array.isArray(entry.author)) {
entry.author.some(author => {
if (author.email && author.name) {
item.push({ author: author.email + ' (' + author.name + ')' })
return true
} else {
return false
}
})
}
if(entry.image) {
item.push({ enclosure: [{ _attr: { url: entry.image } }] });
}
channel.push({ item });
})
if(isContent) {
rss[0]._attr['xmlns:content'] = 'http://purl.org/rss/1.0/modules/content/';
}
if(isAtom) {
rss[0]._attr['xmlns:atom'] = 'http://www.w3.org/2005/Atom';
}
return DOCTYPE + xml(root, true);
}
json1() {
const { options, items, extensions } = this
let feed = {
version: 'https://jsonfeed.org/version/1',
title: options.title,
};
if (options.link) {
feed.home_page_url = options.link;
}
if (options.feedLinks && options.feedLinks.json) {
feed.feed_url = options.feedLinks.json;
}
if (options.description) {
feed.description = options.description;
}
if (options.image) {
feed.icon = options.image;
}
if (options.author) {
feed.author = {};
if (options.author.name) {
feed.author.name = options.author.name;
}
if (options.author.link) {
feed.author.url = options.author.link;
}
}
extensions.forEach(e => {
feed[e.name] = e.objects;
});
feed.items = items.map(item => {
let feedItem = {
id: item.id,
// json_feed distinguishes between html and text content
// but since we only take a single type, we'll assume HTML
html_content: item.content,
}
if (item.link) {
feedItem.url = item.link;
}
if(item.title) {
feedItem.title = item.title;
}
if (item.description) {
feedItem.summary = item.description;
}
if (item.image) {
feedItem.image = item.image
}
if (item.date) {
feedItem.date_modified = this.ISODateString(item.date);
}
if (item.published) {
feedItem.date_published = this.ISODateString(item.published);
}
if (item.author) {
let author = item.author;
if (author instanceof Array) {
// json feed only supports 1 author per post
author = author[0];
}
feedItem.author = {};
if (author.name) {
feedItem.author.name = author.name;
}
if (author.link) {
feedItem.author.url = author.link;
}
}
if(item.extensions) {
item.extensions.forEach(e => {
feedItem[e.name] = e.objects;
});
}
return feedItem;
});
return JSON.stringify(feed, null, 4);
}
ISODateString(d) {
function pad(n) {
return n<10 ? '0'+n : n
}
return d.getUTCFullYear() + '-'
+ pad(d.getUTCMonth() + 1) + '-'
+ pad(d.getUTCDate()) + 'T'
+ pad(d.getUTCHours()) + ':'
+ pad(d.getUTCMinutes()) + ':'
+ pad(d.getUTCSeconds()) + 'Z'
}
}
module.exports = Feed