Commit d2772256 authored by Alexandra Rogova's avatar Alexandra Rogova

Milestone 2

parent 17cb4353
......@@ -20,7 +20,6 @@ module.exports = {
var invoked = false,
to_run = args.shift();
console.log(to_run.args[1]);
process = childProcess.fork(to_run.scriptPath, to_run.args);
process.on('message', function (mes){
......
const fs = require('fs');
const util = require('util');
const readdir = util.promisify(fs.readdir);
const Rss_parser = require('rss-parser');
var rss_parser = new Rss_parser();
var xml_parser = require('xml2js').parseString;
var to_run_sitemap,
sitemap_stream;
function run(script, files, stream, callback){
var childProcess = require('child_process');
if (files.length === 0) return;
var invoked = false,
to_run = files.shift();
process = childProcess.fork(script, ['-f', to_run]);
process.on('message', function (mes){
mes = mes.toString();
fs.readFile(to_run, (err, data) => {
if (to_run.includes("rss")){
rss_parser.parseString(data, (err, result) => {
stream.write(to_run +","+ result.items.length +","+ mes + "\n");
});
} else {
xml_parser(data, (err, result) => {
stream.write(to_run +","+ result.urlset.url.length.toString() +","+ mes + "\n");
});
}
});
run(script, files, stream, callback);
});
process.on('error', function (err) {
if (invoked) return;
invoked = true;
callback(err);
});
process.on('exit', function (code) {
callback();
});
}
function run_sitemap(){
run("./tests/test_sitemap.js", to_run_sitemap, sitemap_stream, function (){return;});
}
(async () => {
fs.truncateSync("./results/rss.csv");
fs.truncateSync("./results/sitemap.csv");
var rss_stream = fs.createWriteStream("./results/rss.csv", {flags:'a'});
sitemap_stream = fs.createWriteStream("./results/sitemap.csv", {flags:'a'});
rss_stream.write("file name,items,items in index, title found, content complete\n");
sitemap_stream.write("file name,items,items in index, title found, content complete\n");
var rss_files = await readdir("./files/RSS");
to_run_rss = rss_files.map(file => "./files/RSS/" + file);
var sitemap_files = await readdir("./files/SITEMAP");
to_run_sitemap = sitemap_files.map(file => "./files/SITEMAP/" + file);
run("./tests/test_rss.js", to_run_rss, rss_stream, run_sitemap);
})();
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
file name,items,items in index, title found, content complete
./files/RSS/rss_100.xml,100,100,true,true
file name,items,items in index, title found, content complete
./files/SITEMAP/sitemap_100.xml,100,86,true,true
const puppeteer = require('puppeteer');
const Server = require('ws').Server;
const args = require("yargs")
.usage("Usage : test_rss.js -file file_path")
.demandOption(['file'])
.alias("f", "file")
.describe("file", "path to the RSS File")
.nargs("file", 1)
.argv;
var browser;
function init_server (){
var ws = new Server({port: 9030});
ws.on('connection', function(w){
w.on('message', function(msg){
ws.close();
process.send(msg);
browser.close();
});
w.on('close', function() {
ws.close();
});
});
}
(async () => {
init_server();
browser = await puppeteer.launch();
var page = await browser.newPage();
await page.goto('https://softinst115787.host.vifib.net/public/unit_tests/test_rss.html');
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.click('input#load')
]);
await fileChooser.accept([args.file]);
})();
const puppeteer = require('puppeteer');
const Server = require('ws').Server;
const args = require("yargs")
.usage("Usage : test_sitemap.js -file file_path")
.demandOption(['file'])
.alias("f", "file")
.describe("file", "path to the Sitemap File")
.nargs("file", 1)
.argv;
var browser;
function init_server (){
var ws = new Server({port: 9030});
ws.on('connection', function(w){
w.on('message', function(msg){
ws.close();
process.send(msg);
browser.close();
});
w.on('close', function() {
ws.close();
});
});
}
(async () => {
init_server();
browser = await puppeteer.launch();
var page = await browser.newPage();
await page.goto('https://softinst115787.host.vifib.net/public/unit_tests/test_sitemap.html');
const [fileChooser] = await Promise.all([
page.waitForFileChooser(),
page.click('input#load')
]);
await fileChooser.accept([args.file]);
})();
sudo: true
language: node_js
node_js:
- 8
script: npm run coveralls
Copyright (c) Felix Böhm
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
var encode = require("./lib/encode.js"),
decode = require("./lib/decode.js");
exports.decode = function(data, level) {
return (!level || level <= 0 ? decode.XML : decode.HTML)(data);
};
exports.decodeStrict = function(data, level) {
return (!level || level <= 0 ? decode.XML : decode.HTMLStrict)(data);
};
exports.encode = function(data, level) {
return (!level || level <= 0 ? encode.XML : encode.HTML)(data);
};
exports.encodeXML = encode.XML;
exports.encodeHTML4 = exports.encodeHTML5 = exports.encodeHTML = encode.HTML;
exports.decodeXML = exports.decodeXMLStrict = decode.XML;
exports.decodeHTML4 = exports.decodeHTML5 = exports.decodeHTML = decode.HTML;
exports.decodeHTML4Strict = exports.decodeHTML5Strict = exports.decodeHTMLStrict = decode.HTMLStrict;
exports.escape = encode.escape;
var entityMap = require("../maps/entities.json"),
legacyMap = require("../maps/legacy.json"),
xmlMap = require("../maps/xml.json"),
decodeCodePoint = require("./decode_codepoint.js");
var decodeXMLStrict = getStrictDecoder(xmlMap),
decodeHTMLStrict = getStrictDecoder(entityMap);
function getStrictDecoder(map) {
var keys = Object.keys(map).join("|"),
replace = getReplacer(map);
keys += "|#[xX][\\da-fA-F]+|#\\d+";
var re = new RegExp("&(?:" + keys + ");", "g");
return function(str) {
return String(str).replace(re, replace);
};
}
var decodeHTML = (function() {
var legacy = Object.keys(legacyMap).sort(sorter);
var keys = Object.keys(entityMap).sort(sorter);
for (var i = 0, j = 0; i < keys.length; i++) {
if (legacy[j] === keys[i]) {
keys[i] += ";?";
j++;
} else {
keys[i] += ";";
}
}
var re = new RegExp("&(?:" + keys.join("|") + "|#[xX][\\da-fA-F]+;?|#\\d+;?)", "g"),
replace = getReplacer(entityMap);
function replacer(str) {
if (str.substr(-1) !== ";") str += ";";
return replace(str);
}
//TODO consider creating a merged map
return function(str) {
return String(str).replace(re, replacer);
};
})();
function sorter(a, b) {
return a < b ? 1 : -1;
}
function getReplacer(map) {
return function replace(str) {
if (str.charAt(1) === "#") {
if (str.charAt(2) === "X" || str.charAt(2) === "x") {
return decodeCodePoint(parseInt(str.substr(3), 16));
}
return decodeCodePoint(parseInt(str.substr(2), 10));
}
return map[str.slice(1, -1)];
};
}
module.exports = {
XML: decodeXMLStrict,
HTML: decodeHTML,
HTMLStrict: decodeHTMLStrict
};
var decodeMap = require("../maps/decode.json");
module.exports = decodeCodePoint;
// modified version of https://github.com/mathiasbynens/he/blob/master/src/he.js#L94-L119
function decodeCodePoint(codePoint) {
if ((codePoint >= 0xd800 && codePoint <= 0xdfff) || codePoint > 0x10ffff) {
return "\uFFFD";
}
if (codePoint in decodeMap) {
codePoint = decodeMap[codePoint];
}
var output = "";
if (codePoint > 0xffff) {
codePoint -= 0x10000;
output += String.fromCharCode(((codePoint >>> 10) & 0x3ff) | 0xd800);
codePoint = 0xdc00 | (codePoint & 0x3ff);
}
output += String.fromCharCode(codePoint);
return output;
}
var inverseXML = getInverseObj(require("../maps/xml.json")),
xmlReplacer = getInverseReplacer(inverseXML);
exports.XML = getInverse(inverseXML, xmlReplacer);
var inverseHTML = getInverseObj(require("../maps/entities.json")),
htmlReplacer = getInverseReplacer(inverseHTML);
exports.HTML = getInverse(inverseHTML, htmlReplacer);
function getInverseObj(obj) {
return Object.keys(obj)
.sort()
.reduce(function(inverse, name) {
inverse[obj[name]] = "&" + name + ";";
return inverse;
}, {});
}
function getInverseReplacer(inverse) {
var single = [],
multiple = [];
Object.keys(inverse).forEach(function(k) {
if (k.length === 1) {
single.push("\\" + k);
} else {
multiple.push(k);
}
});
//TODO add ranges
multiple.unshift("[" + single.join("") + "]");
return new RegExp(multiple.join("|"), "g");
}
var re_nonASCII = /[^\0-\x7F]/g,
re_astralSymbols = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
function singleCharReplacer(c) {
return (
"&#x" +
c
.charCodeAt(0)
.toString(16)
.toUpperCase() +
";"
);
}
function astralReplacer(c) {
// http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
var high = c.charCodeAt(0);
var low = c.charCodeAt(1);
var codePoint = (high - 0xd800) * 0x400 + low - 0xdc00 + 0x10000;
return "&#x" + codePoint.toString(16).toUpperCase() + ";";
}
function getInverse(inverse, re) {
function func(name) {
return inverse[name];
}
return function(data) {
return data
.replace(re, func)
.replace(re_astralSymbols, astralReplacer)
.replace(re_nonASCII, singleCharReplacer);
};
}
var re_xmlChars = getInverseReplacer(inverseXML);
function escapeXML(data) {
return data
.replace(re_xmlChars, singleCharReplacer)
.replace(re_astralSymbols, astralReplacer)
.replace(re_nonASCII, singleCharReplacer);
}
exports.escape = escapeXML;
{"0":65533,"128":8364,"130":8218,"131":402,"132":8222,"133":8230,"134":8224,"135":8225,"136":710,"137":8240,"138":352,"139":8249,"140":338,"142":381,"145":8216,"146":8217,"147":8220,"148":8221,"149":8226,"150":8211,"151":8212,"152":732,"153":8482,"154":353,"155":8250,"156":339,"158":382,"159":376}
\ No newline at end of file
This diff is collapsed.
{"Aacute":"\u00C1","aacute":"\u00E1","Acirc":"\u00C2","acirc":"\u00E2","acute":"\u00B4","AElig":"\u00C6","aelig":"\u00E6","Agrave":"\u00C0","agrave":"\u00E0","amp":"&","AMP":"&","Aring":"\u00C5","aring":"\u00E5","Atilde":"\u00C3","atilde":"\u00E3","Auml":"\u00C4","auml":"\u00E4","brvbar":"\u00A6","Ccedil":"\u00C7","ccedil":"\u00E7","cedil":"\u00B8","cent":"\u00A2","copy":"\u00A9","COPY":"\u00A9","curren":"\u00A4","deg":"\u00B0","divide":"\u00F7","Eacute":"\u00C9","eacute":"\u00E9","Ecirc":"\u00CA","ecirc":"\u00EA","Egrave":"\u00C8","egrave":"\u00E8","ETH":"\u00D0","eth":"\u00F0","Euml":"\u00CB","euml":"\u00EB","frac12":"\u00BD","frac14":"\u00BC","frac34":"\u00BE","gt":">","GT":">","Iacute":"\u00CD","iacute":"\u00ED","Icirc":"\u00CE","icirc":"\u00EE","iexcl":"\u00A1","Igrave":"\u00CC","igrave":"\u00EC","iquest":"\u00BF","Iuml":"\u00CF","iuml":"\u00EF","laquo":"\u00AB","lt":"<","LT":"<","macr":"\u00AF","micro":"\u00B5","middot":"\u00B7","nbsp":"\u00A0","not":"\u00AC","Ntilde":"\u00D1","ntilde":"\u00F1","Oacute":"\u00D3","oacute":"\u00F3","Ocirc":"\u00D4","ocirc":"\u00F4","Ograve":"\u00D2","ograve":"\u00F2","ordf":"\u00AA","ordm":"\u00BA","Oslash":"\u00D8","oslash":"\u00F8","Otilde":"\u00D5","otilde":"\u00F5","Ouml":"\u00D6","ouml":"\u00F6","para":"\u00B6","plusmn":"\u00B1","pound":"\u00A3","quot":"\"","QUOT":"\"","raquo":"\u00BB","reg":"\u00AE","REG":"\u00AE","sect":"\u00A7","shy":"\u00AD","sup1":"\u00B9","sup2":"\u00B2","sup3":"\u00B3","szlig":"\u00DF","THORN":"\u00DE","thorn":"\u00FE","times":"\u00D7","Uacute":"\u00DA","uacute":"\u00FA","Ucirc":"\u00DB","ucirc":"\u00FB","Ugrave":"\u00D9","ugrave":"\u00F9","uml":"\u00A8","Uuml":"\u00DC","uuml":"\u00FC","Yacute":"\u00DD","yacute":"\u00FD","yen":"\u00A5","yuml":"\u00FF"}
\ No newline at end of file
{"amp":"&","apos":"'","gt":">","lt":"<","quot":"\""}
{
"_from": "entities@^1.1.1",
"_id": "entities@1.1.2",
"_inBundle": false,
"_integrity": "sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==",
"_location": "/entities",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "entities@^1.1.1",
"name": "entities",
"escapedName": "entities",
"rawSpec": "^1.1.1",
"saveSpec": null,
"fetchSpec": "^1.1.1"
},
"_requiredBy": [
"/rss-parser"
],
"_resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz",
"_shasum": "bdfa735299664dfafd34529ed4f8522a275fea56",
"_spec": "entities@^1.1.1",
"_where": "C:\\Users\\thequ\\Documents\\Mynij\\Mynij-unit-tests\\node_modules\\rss-parser",
"author": {
"name": "Felix Boehm",
"email": "me@feedic.com"
},
"bugs": {
"url": "https://github.com/fb55/entities/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "Encode & decode XML/HTML entities with ease",
"devDependencies": {
"coveralls": "*",
"istanbul": "*",
"jshint": "2",
"mocha": "^5.0.1",
"mocha-lcov-reporter": "*"
},
"directories": {
"test": "test"
},
"homepage": "https://github.com/fb55/entities#readme",
"jshintConfig": {
"eqeqeq": true,
"freeze": true,
"latedef": "nofunc",
"noarg": true,
"nonbsp": true,
"quotmark": "double",
"undef": true,
"unused": true,
"trailing": true,
"eqnull": true,
"proto": true,
"smarttabs": true,
"node": true,
"globals": {
"describe": true,
"it": true
}
},
"keywords": [
"html",
"xml",
"entity",
"decoding",
"encoding"
],
"license": "BSD-2-Clause",
"main": "./index.js",
"name": "entities",
"prettier": {
"tabWidth": 4
},
"repository": {
"type": "git",
"url": "git://github.com/fb55/entities.git"
},
"scripts": {
"coveralls": "npm run lint && npm run lcov && (cat coverage/lcov.info | coveralls || exit 0)",
"lcov": "istanbul cover _mocha --report lcovonly -- -R spec",
"lint": "jshint index.js lib/*.js test/*.js",
"test": "mocha && npm run lint"
},
"version": "1.1.2"
}
# entities [![NPM version](http://img.shields.io/npm/v/entities.svg)](https://npmjs.org/package/entities) [![Downloads](https://img.shields.io/npm/dm/entities.svg)](https://npmjs.org/package/entities) [![Build Status](http://img.shields.io/travis/fb55/entities.svg)](http://travis-ci.org/fb55/entities) [![Coverage](http://img.shields.io/coveralls/fb55/entities.svg)](https://coveralls.io/r/fb55/entities)
En- & decoder for XML/HTML entities.
## How to…
### …install `entities`
npm i entities
### …use `entities`
```javascript
var entities = require("entities");
//encoding
entities.encodeXML("&#38;"); // "&amp;#38;"
entities.encodeHTML("&#38;"); // "&amp;&num;38&semi;"
//decoding
entities.decodeXML("asdf &amp; &#xFF; &#xFC; &apos;"); // "asdf & ÿ ü '"
entities.decodeHTML("asdf &amp; &yuml; &uuml; &apos;"); // "asdf & ÿ ü '"
```
<!-- TODO extend API -->
---
License: BSD-2-Clause
--check-leaks
--reporter spec
var assert = require("assert"),
path = require("path"),
entities = require("../");
describe("Encode->decode test", function() {
var testcases = [
{
input: "asdf & ÿ ü '",
xml: "asdf &amp; &#xFF; &#xFC; &apos;",
html: "asdf &amp; &yuml; &uuml; &apos;"
},
{
input: "&#38;",
xml: "&amp;#38;",
html: "&amp;&num;38&semi;"
}
];
testcases.forEach(function(tc) {
var encodedXML = entities.encodeXML(tc.input);
it("should XML encode " + tc.input, function() {
assert.equal(encodedXML, tc.xml);
});
it("should default to XML encode " + tc.input, function() {
assert.equal(entities.encode(tc.input), tc.xml);
});
it("should XML decode " + encodedXML, function() {
assert.equal(entities.decodeXML(encodedXML), tc.input);
});
it("should default to XML encode " + encodedXML, function() {
assert.equal(entities.decode(encodedXML), tc.input);
});
it("should default strict to XML encode " + encodedXML, function() {
assert.equal(entities.decodeStrict(encodedXML), tc.input);
});
var encodedHTML5 = entities.encodeHTML5(tc.input);
it("should HTML5 encode " + tc.input, function() {
assert.equal(encodedHTML5, tc.html);
});
it("should HTML5 decode " + encodedHTML5, function() {
assert.equal(entities.decodeHTML(encodedHTML5), tc.input);
});
});
it("should encode data URIs (issue 16)", function() {
var data = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAALAAABAAEAAAIBRAA7";
assert.equal(entities.decode(entities.encode(data)), data);
});
});
describe("Decode test", function() {
var testcases = [
{ input: "&amp;amp;", output: "&amp;" },
{ input: "&amp;#38;", output: "&#38;" },
{ input: "&amp;#x26;", output: "&#x26;" },
{ input: "&amp;#X26;", output: "&#X26;" },
{ input: "&#38;#38;", output: "&#38;" },
{ input: "&#x26;#38;", output: "&#38;" },
{ input: "&#X26;#38;", output: "&#38;" },
{ input: "&#x3a;", output: ":" },
{ input: "&#x3A;", output: ":" },
{ input: "&#X3a;", output: ":" },
{ input: "&#X3A;", output: ":" }
];
testcases.forEach(function(tc) {
it("should XML decode " + tc.input, function() {
assert.equal(entities.decodeXML(tc.input), tc.output);
});
it("should HTML4 decode " + tc.input, function() {
assert.equal(entities.decodeHTML(tc.input), tc.output);
});
it("should HTML5 decode " + tc.input, function() {
assert.equal(entities.decodeHTML(tc.input), tc.output);
});
});
});
var levels = ["xml", "entities"];
describe("Documents", function() {
levels
.map(function(n) {
return path.join("..", "maps", n);
})
.map(require)
.forEach(function(doc, i) {
describe("Decode", function() {
it(levels[i], function() {
Object.keys(doc).forEach(function(e) {
for (var l = i; l < levels.length; l++) {
assert.equal(entities.decode("&" + e + ";", l), doc[e]);
}
});
});
});
describe("Decode strict", function() {
it(levels[i], function() {
Object.keys(doc).forEach(function(e) {
for (var l = i; l < levels.length; l++) {
assert.equal(entities.decodeStrict("&" + e + ";", l), doc[e]);
}
});
});
});
describe("Encode", function() {
it(levels[i], function() {
Object.keys(doc).forEach(function(e) {
for (var l = i; l < levels.length; l++) {
assert.equal(entities.decode(entities.encode(doc[e], l), l), doc[e]);
}
});
});
});
});
var legacy = require("../maps/legacy.json");
describe("Legacy", function() {
it("should decode", runLegacy);
});
function runLegacy() {
Object.keys(legacy).forEach(function(e) {
assert.equal(entities.decodeHTML("&" + e), legacy[e]);
});
}
});
var astral = {
"1D306": "\uD834\uDF06",
"1D11E": "\uD834\uDD1E"
};
var astralSpecial = {
"80": "\u20AC",
"110000": "\uFFFD"
};
describe("Astral entities", function() {
Object.keys(astral).forEach(function(c) {
it("should decode " + astral[c], function() {
assert.equal(entities.decode("&#x" + c + ";"), astral[c]);
});
it("should encode " + astral[c], function() {
assert.equal(entities.encode(astral[c]), "&#x" + c + ";");
});
it("should escape " + astral[c], function() {
assert.equal(entities.escape(astral[c]), "&#x" + c + ";");
});
});
Object.keys(astralSpecial).forEach(function(c) {
it("special should decode \\u" + c, function() {
assert.equal(entities.decode("&#x" + c + ";"), astralSpecial[c]);
});
});
});
describe("Escape", function() {
it("should always decode ASCII chars", function() {
for (var i = 0; i < 0x7f; i++) {
var c = String.fromCharCode(i);
assert.equal(entities.decodeXML(entities.escape(c)), c);
}
});
});
[1016/142630.878:WARNING:chrome_test_launcher.cc(100)] Failed to add Windows firewall rules -- Windows firewall dialogs may appear.
[0103/175702.095:ERROR:exception_handler_server.cc(527)] ConnectNamedPipe: The pipe is being closed. (0xE8)
[0103/175804.522:ERROR:exception_handler_server.cc(527)] ConnectNamedPipe: The pipe is being closed. (0xE8)
dist: trusty
language: node_js
node_js:
- "8"
before_script:
- npm install -g mocha
script: npm test
addons:
apt:
packages:
- libnss3
MIT License
Copyright (c) 2016 Bobby Brennan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
# rss-parser
[![Version][npm-image]][npm-link]
[![Build Status][build-image]][build-link]
[![Downloads][downloads-image]][npm-link]
[downloads-image]: https://img.shields.io/npm/dm/rss-parser.svg
[npm-image]: https://img.shields.io/npm/v/rss-parser.svg
[npm-link]: https://npmjs.org/package/rss-parser
[build-image]: https://travis-ci.org/bobby-brennan/rss-parser.svg?branch=master
[build-link]: https://travis-ci.org/bobby-brennan/rss-parser
A small library for turning RSS XML feeds into JavaScript objects.
## Installation
```bash
npm install --save rss-parser
```
## Usage
You can parse RSS from a URL (`parser.parseURL`) or an XML string (`parser.parseString`).
Both callbacks and Promises are supported.
### NodeJS
Here's an example in NodeJS using Promises with async/await:
```js
let Parser = require('rss-parser');
let parser = new Parser();
(async () => {
let feed = await parser.parseURL('https://www.reddit.com/.rss');
console.log(feed.title);
feed.items.forEach(item => {
console.log(item.title + ':' + item.link)
});
})();
```
### Web
> We recommend using a bundler like [webpack](https://webpack.js.org/), but we also provide
> pre-built browser distributions in the `dist/` folder. If you use the pre-built distribution,
> you'll need a [polyfill](https://github.com/taylorhakes/promise-polyfill) for Promise support.
Here's an example in the browser using callbacks:
```html
<script src="/node_modules/rss-parser/dist/rss-parser.min.js"></script>
<script>
// Note: some RSS feeds can't be loaded in the browser due to CORS security.
// To get around this, you can use a proxy.
const CORS_PROXY = "https://cors-anywhere.herokuapp.com/"
let parser = new RSSParser();
parser.parseURL(CORS_PROXY + 'https://www.reddit.com/.rss', function(err, feed) {
if (err) throw err;
console.log(feed.title);
feed.items.forEach(function(entry) {
console.log(entry.title + ':' + entry.link);
})
})
</script>
```
### Upgrading from v2 to v3
A few minor breaking changes were made in v3. Here's what you need to know:
* You need to construct a `new Parser()` before calling `parseString` or `parseURL`
* `parseFile` is no longer available (for better browser support)
* `options` are now passed to the Parser constructor
* `parsed.feed` is now just `feed` (top-level object removed)
* `feed.entries` is now `feed.items` (to better match RSS XML)
## Output
Check out the full output format in [test/output/reddit.json](test/output/reddit.json)
```yaml
feedUrl: 'https://www.reddit.com/.rss'
title: 'reddit: the front page of the internet'
description: ""
link: 'https://www.reddit.com/'
items:
- title: 'The water is too deep, so he improvises'
link: 'https://www.reddit.com/r/funny/comments/3skxqc/the_water_is_too_deep_so_he_improvises/'
pubDate: 'Thu, 12 Nov 2015 21:16:39 +0000'
creator: "John Doe"
content: '<a href="http://example.com">this is a link</a> &amp; <b>this is bold text</b>'
contentSnippet: 'this is a link & this is bold text'
guid: 'https://www.reddit.com/r/funny/comments/3skxqc/the_water_is_too_deep_so_he_improvises/'
categories:
- funny
isoDate: '2015-11-12T21:16:39.000Z'
```
##### Notes:
* The `contentSnippet` field strips out HTML tags and unescapes HTML entities
* The `dc:` prefix will be removed from all fields
* Both `dc:date` and `pubDate` will be available in ISO 8601 format as `isoDate`
* If `author` is specified, but not `dc:creator`, `creator` will be set to `author` ([see article](http://www.lowter.com/blogs/2008/2/9/rss-dccreator-author))
* Atom's `updated` becomes `lastBuildDate` for consistency
## XML Options
### Custom Fields
If your RSS feed contains fields that aren't currently returned, you can access them using the `customFields` option.
```js
let parser = new Parser({
customFields: {
feed: ['otherTitle', 'extendedDescription'],
item: ['coAuthor','subtitle'],
}
});
parser.parseURL('https://www.reddit.com/.rss', function(err, feed) {
console.log(feed.extendedDescription);
feed.items.forEach(function(entry) {
console.log(entry.coAuthor + ':' + entry.subtitle);
})
})
```
To rename fields, you can pass in an array with two items, in the format `[fromField, toField]`:
```js
let parser = new Parser({
customFields: {
item: [
['dc:coAuthor', 'coAuthor'],
]
}
})
```
To pass additional flags, provide an object as the third array item. Currently there is one such flag:
* `keepArray`: `true` to return *all* values for fields that can have multiple entries. Default: return the first item only.
```js
let parser = new Parser({
customFields: {
item: [
['media:content', 'media:content', {keepArray: true}],
]
}
})
```
### Default RSS version
If your RSS Feed doesn't contain a `<rss>` tag with a `version` attribute,
you can pass a `defaultRSS` option for the Parser to use:
```js
let parser = new Parser({
defaultRSS: 2.0
});
```
### xml2js passthrough
`rss-parser` uses [xml2js](https://github.com/Leonidas-from-XIV/node-xml2js)
to parse XML. You can pass [these options](https://github.com/Leonidas-from-XIV/node-xml2js#options)
to `new xml2js.Parser()` by specifying `options.xml2js`:
```js
let parser = new Parser({
xml2js: {
emptyTag: '--EMPTY--',
}
});
```
## HTTP Options
### Timeout
You can set the amount of time (in milliseconds) to wait before the HTTP request times out (default 60 seconds):
```js
let parser = new Parser({
timeout: 1000,
});
```
### Headers
You can pass headers to the HTTP request:
```js
let parser = new Parser({
headers: {'User-Agent': 'something different'},
});
```
### Redirects
By default, `parseURL` will follow up to five redirects. You can change this
with `options.maxRedirects`.
```js
let parser = new Parser({maxRedirects: 100});
```
## Contributing
Contributions are welcome! If you are adding a feature or fixing a bug, please be sure to add a [test case](https://github.com/bobby-brennan/rss-parser/tree/master/test/input)
### Running Tests
The tests run the RSS parser for several sample RSS feeds in `test/input` and outputs the resulting JSON into `test/output`. If there are any changes to the output files the tests will fail.
To check if your changes affect the output of any test cases, run
`npm test`
To update the output files with your changes, run
`WRITE_GOLDEN=true npm test`
### Publishing Releases
```bash
npm run build
git commit -a -m "Build distribution"
npm version minor # or major/patch
npm publish
git push --follow-tags
```
{
"name": "rss-parser",
"description": "",
"version": "1.1.0",
"main": "dist/rss-parser.js",
"authors": [
"Bobby Brennan"
],
"license": "MIT",
"homepage": "https://github.com/bobby-brennan/rss-parser",
"moduleType": [
"node"
],
"ignore": [
"**/.*",
"node_modules",
"bower_components",
"test",
"tests"
]
}
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
import { Options } from 'xml2js';
declare namespace Parser {
export interface Headers {
readonly Accept?: string;
readonly 'User-Agent'?: string;
}
export interface CustomFields {
readonly feed?: string[];
readonly item?: string[] | string[][];
}
export interface ParserOptions {
readonly xml2js?: Options;
readonly headers?: Headers;
readonly defaultRSS?: number;
readonly maxRedirects?: number;
readonly customFields?: CustomFields;
readonly timeout?: number;
}
export interface Item {
[key: string]: any;
link?: string;
guid?: string;
title?: string;
pubDate?: string;
creator?: string;
content?: string;
isoDate?: string;
categories?: string[];
contentSnippet?: string;
}
export interface Output {
[key: string]: any;
link?: string;
title?: string;
items?: Item[];
feedUrl?: string;
description?: string;
itunes?: {
[key: string]: any;
image?: string;
owner?: {
name?: string;
email?: string;
};
author?: string;
summary?: string;
explicit?: string;
categories?: string[];
keywords?: string[];
};
}
}
/**
* Class that handles all parsing or URL, or even XML, RSS feed to JSON.
*/
declare class Parser {
/**
* @param options - Parser options.
*/
constructor(options?: Parser.ParserOptions);
/**
* Parse XML content to JSON.
*
* @param xml - The xml to be parsed.
* @param callback - Traditional callback.
*
* @returns Promise that has the same Output as the callback.
*/
parseString(
xml: string,
callback?: (err: Error, feed: Parser.Output) => void
): Promise<Parser.Output>;
/**
* Parse URL content to JSON.
*
* @param feedUrl - The url that needs to be parsed to JSON.
* @param callback - Traditional callback.
* @param redirectCount - Max of redirects, default is set to five.
*
* @example
* await parseURL('https://www.reddit.com/.rss');
* parseURL('https://www.reddit.com/.rss', (err, feed) => { ... });
*
* @returns Promise that has the same Output as the callback.
*/
parseURL(
feedUrl: string,
callback?: (err: Error, feed: Parser.Output) => void,
redirectCount?: number
): Promise<Parser.Output>;
}
export = Parser;
'use strict';
module.exports = require('./lib/parser');
const fields = module.exports = {};
fields.feed = [
['author', 'creator'],
['dc:publisher', 'publisher'],
['dc:creator', 'creator'],
['dc:source', 'source'],
['dc:title', 'title'],
['dc:type', 'type'],
'title',
'description',
'author',
'pubDate',
'webMaster',
'managingEditor',
'generator',
'link',
'language',
'copyright',
'lastBuildDate',
'docs',
'generator',
'ttl',
'rating',
'skipHours',
'skipDays',
];
fields.item = [
['author', 'creator'],
['dc:creator', 'creator'],
['dc:date', 'date'],
['dc:language', 'language'],
['dc:rights', 'rights'],
['dc:source', 'source'],
['dc:title', 'title'],
'title',
'link',
'pubDate',
'author',
'content:encoded',
'enclosure',
'dc:creator',
'dc:date',
'comments',
];
var mapItunesField = function(f) {
return ['itunes:' + f, f];
}
fields.podcastFeed = ([
'author',
'subtitle',
'summary',
'explicit'
]).map(mapItunesField);
fields.podcastItem = ([
'author',
'subtitle',
'summary',
'explicit',
'duration',
'image',
'episode',
'image',
'season',
'keywords',
]).map(mapItunesField);
"use strict";
const http = require('http');
const https = require('https');
const xml2js = require('xml2js');
const url = require('url');
const fields = require('./fields');
const utils = require('./utils');
const DEFAULT_HEADERS = {
'User-Agent': 'rss-parser',
'Accept': 'application/rss+xml',
}
const DEFAULT_MAX_REDIRECTS = 5;
const DEFAULT_TIMEOUT = 60000;
class Parser {
constructor(options={}) {
options.headers = options.headers || {};
options.xml2js = options.xml2js || {};
options.customFields = options.customFields || {};
options.customFields.item = options.customFields.item || [];
options.customFields.feed = options.customFields.feed || [];
if (!options.maxRedirects) options.maxRedirects = DEFAULT_MAX_REDIRECTS;
if (!options.timeout) options.timeout = DEFAULT_TIMEOUT;
this.options = options;
this.xmlParser = new xml2js.Parser(this.options.xml2js);
}
parseString(xml, callback) {
let prom = new Promise((resolve, reject) => {
this.xmlParser.parseString(xml, (err, result) => {
if (err) return reject(err);
if (!result) {
return reject(new Error('Unable to parse XML.'));
}
let feed = null;
if (result.feed) {
feed = this.buildAtomFeed(result);
} else if (result.rss && result.rss.$ && result.rss.$.version && result.rss.$.version.match(/^2/)) {
feed = this.buildRSS2(result);
} else if (result['rdf:RDF']) {
feed = this.buildRSS1(result);
} else if (result.rss && result.rss.$ && result.rss.$.version && result.rss.$.version.match(/0\.9/)) {
feed = this.buildRSS0_9(result);
} else if (result.rss && this.options.defaultRSS) {
switch(this.options.defaultRSS) {
case 0.9:
feed = this.buildRSS0_9(result);
break;
case 1:
feed = this.buildRSS1(result);
break;
case 2:
feed = this.buildRSS2(result);
break;
default:
return reject(new Error("default RSS version not recognized."))
}
} else {
return reject(new Error("Feed not recognized as RSS 1 or 2."))
}
resolve(feed);
});
});
prom = utils.maybePromisify(callback, prom);
return prom;
}
parseURL(feedUrl, callback, redirectCount=0) {
let xml = '';
let get = feedUrl.indexOf('https') === 0 ? https.get : http.get;
let urlParts = url.parse(feedUrl);
let headers = Object.assign({}, DEFAULT_HEADERS, this.options.headers);
let prom = new Promise((resolve, reject) => {
let req = get({
headers,
auth: urlParts.auth,
protocol: urlParts.protocol,
hostname: urlParts.hostname,
port: urlParts.port,
path: urlParts.path,
}, (res) => {
if (this.options.maxRedirects && res.statusCode >= 300 && res.statusCode < 400 && res.headers['location']) {
if (redirectCount === this.options.maxRedirects) {
return reject(new Error("Too many redirects"));
} else {
const newLocation = url.resolve(feedUrl, res.headers['location']);
return this.parseURL(newLocation, null, redirectCount + 1).then(resolve, reject);
}
} else if (res.statusCode >= 300) {
return reject(new Error("Status code " + res.statusCode))
}
let encoding = utils.getEncodingFromContentType(res.headers['content-type']);
res.setEncoding(encoding);
res.on('data', (chunk) => {
xml += chunk;
});
res.on('end', () => {
return this.parseString(xml).then(resolve, reject);
});
})
req.setTimeout(this.options.timeout, () => {
return reject(new Error("Request timed out after " + this.options.timeout + "ms"));
});
req.on('error', reject);
});
prom = utils.maybePromisify(callback, prom);
return prom;
}
buildAtomFeed(xmlObj) {
let feed = {items: []};
utils.copyFromXML(xmlObj.feed, feed, this.options.customFields.feed);
if (xmlObj.feed.link) {
feed.link = utils.getLink(xmlObj.feed.link, 'alternate', 0);
feed.feedUrl = utils.getLink(xmlObj.feed.link, 'self', 1);
}
if (xmlObj.feed.title) {
let title = xmlObj.feed.title[0] || '';
if (title._) title = title._
if (title) feed.title = title;
}
if (xmlObj.feed.updated) {
feed.lastBuildDate = xmlObj.feed.updated[0];
}
(xmlObj.feed.entry || []).forEach(entry => {
let item = {};
utils.copyFromXML(entry, item, this.options.customFields.item);
if (entry.title) {
let title = entry.title[0] || '';
if (title._) title = title._;
if (title) item.title = title;
}
if (entry.link && entry.link.length) {
item.link = utils.getLink(entry.link, 'alternate', 0);
}
if (entry.published && entry.published.length && entry.published[0].length) item.pubDate = new Date(entry.published[0]).toISOString();
if (!item.pubDate && entry.updated && entry.updated.length && entry.updated[0].length) item.pubDate = new Date(entry.updated[0]).toISOString();
if (entry.author && entry.author.length) item.author = entry.author[0].name[0];
if (entry.content && entry.content.length) {
item.content = utils.getContent(entry.content[0]);
item.contentSnippet = utils.getSnippet(item.content)
}
if (entry.id) {
item.id = entry.id[0];
}
this.setISODate(item);
feed.items.push(item);
});
return feed;
}
buildRSS0_9(xmlObj) {
var channel = xmlObj.rss.channel[0];
var items = channel.item;
return this.buildRSS(channel, items);
}
buildRSS1(xmlObj) {
xmlObj = xmlObj['rdf:RDF'];
let channel = xmlObj.channel[0];
let items = xmlObj.item;
return this.buildRSS(channel, items);
}
buildRSS2(xmlObj) {
let channel = xmlObj.rss.channel[0];
let items = channel.item;
let feed = this.buildRSS(channel, items);
if (xmlObj.rss.$ && xmlObj.rss.$['xmlns:itunes']) {
this.decorateItunes(feed, channel);
}
return feed;
}
buildRSS(channel, items) {
items = items || [];
let feed = {items: []};
let feedFields = fields.feed.concat(this.options.customFields.feed);
let itemFields = fields.item.concat(this.options.customFields.item);
if (channel['atom:link'] && channel['atom:link'][0] && channel['atom:link'][0].$) {
feed.feedUrl = channel['atom:link'][0].$.href;
}
if (channel.image && channel.image[0] && channel.image[0].url) {
feed.image = {};
let image = channel.image[0];
if (image.link) feed.image.link = image.link[0];
if (image.url) feed.image.url = image.url[0];
if (image.title) feed.image.title = image.title[0];
if (image.width) feed.image.width = image.width[0];
if (image.height) feed.image.height = image.height[0];
}
utils.copyFromXML(channel, feed, feedFields);
items.forEach(xmlItem => {
let item = {};
utils.copyFromXML(xmlItem, item, itemFields);
if (xmlItem.enclosure) {
item.enclosure = xmlItem.enclosure[0].$;
}
if (xmlItem.description) {
item.content = utils.getContent(xmlItem.description[0]);
item.contentSnippet = utils.getSnippet(item.content);
}
if (xmlItem.guid) {
item.guid = xmlItem.guid[0];
if (item.guid._) item.guid = item.guid._;
}
if (xmlItem.category) item.categories = xmlItem.category;
this.setISODate(item);
feed.items.push(item);
});
return feed;
}
/**
* Add iTunes specific fields from XML to extracted JSON
*
* @access public
* @param {object} feed extracted
* @param {object} channel parsed XML
*/
decorateItunes(feed, channel) {
let items = channel.item || [];
let categories = [];
feed.itunes = {}
if (channel['itunes:owner']) {
let owner = {};
if(channel['itunes:owner'][0]['itunes:name']) {
owner.name = channel['itunes:owner'][0]['itunes:name'][0];
}
if(channel['itunes:owner'][0]['itunes:email']) {
owner.email = channel['itunes:owner'][0]['itunes:email'][0];
}
feed.itunes.owner = owner;
}
if (channel['itunes:image']) {
let image;
let hasImageHref = (channel['itunes:image'][0] &&
channel['itunes:image'][0].$ &&
channel['itunes:image'][0].$.href);
image = hasImageHref ? channel['itunes:image'][0].$.href : null;
if (image) {
feed.itunes.image = image;
}
}
if(channel['itunes:category']) {
channel['itunes:category'].forEach((category) => {
categories.push(category.$.text);
});
feed.itunes.categories = categories;
}
if(channel['itunes:keywords']) {
let keywords=channel['itunes:keywords'][0];
if(keywords && typeof keywords._ === 'string'){
keywords=keywords._
}
if(keywords)feed.itunes.keywords = keywords.split(",");
}
utils.copyFromXML(channel, feed.itunes, fields.podcastFeed);
items.forEach((item, index) => {
let entry = feed.items[index];
entry.itunes = {};
utils.copyFromXML(item, entry.itunes, fields.podcastItem);
let image = item['itunes:image'];
if (image && image[0] && image[0].$ && image[0].$.href) {
entry.itunes.image = image[0].$.href;
}
});
}
setISODate(item) {
let date = item.pubDate || item.date;
if (date) {
try {
item.isoDate = new Date(date.trim()).toISOString();
} catch (e) {
// Ignore bad date format
}
}
}
}
module.exports = Parser;
const utils = module.exports = {};
const entities = require('entities');
const xml2js = require('xml2js');
utils.stripHtml = function(str) {
return str.replace(/<(?:.|\n)*?>/gm, '');
}
utils.getSnippet = function(str) {
return entities.decode(utils.stripHtml(str)).trim();
}
utils.getLink = function(links, rel, fallbackIdx) {
if (!links) return;
for (let i = 0; i < links.length; ++i) {
if (links[i].$.rel === rel) return links[i].$.href;
}
if (links[fallbackIdx]) return links[fallbackIdx].$.href;
}
utils.getContent = function(content) {
if (typeof content._ === 'string') {
return content._;
} else if (typeof content === 'object') {
let builder = new xml2js.Builder({headless: true, explicitRoot: true, rootName: 'div', renderOpts: {pretty: false}});
return builder.buildObject(content);
} else {
return content;
}
}
utils.copyFromXML = function(xml, dest, fields) {
fields.forEach(function(f) {
let from = f;
let to = f;
let options = {};
if (Array.isArray(f)) {
from = f[0];
to = f[1];
if (f.length > 2) {
options = f[2];
}
}
const { keepArray } = options;
if (xml[from] !== undefined){
dest[to] = keepArray ? xml[from] : xml[from][0];
}
if (dest[to] && typeof dest[to]._ === 'string') {
dest[to]=dest[to]._;
}
})
}
utils.maybePromisify = function(callback, promise) {
if (!callback) return promise;
return promise.then(
data => setTimeout(() => callback(null, data)),
err => setTimeout(() => callback(err))
);
}
const DEFAULT_ENCODING = 'utf8';
const ENCODING_REGEX = /(encoding|charset)\s*=\s*(\S+)/;
const SUPPORTED_ENCODINGS = ['ascii', 'utf8', 'utf16le', 'ucs2', 'base64', 'latin1', 'binary', 'hex'];
const ENCODING_ALIASES = {
'utf-8': 'utf8',
'iso-8859-1': 'latin1',
}
utils.getEncodingFromContentType = function(contentType) {
contentType = contentType || '';
let match = contentType.match(ENCODING_REGEX);
let encoding = (match || [])[2] || '';
encoding = encoding.toLowerCase();
encoding = ENCODING_ALIASES[encoding] || encoding;
if (!encoding || SUPPORTED_ENCODINGS.indexOf(encoding) === -1) {
encoding = DEFAULT_ENCODING;
}
return encoding;
}
{
"_from": "rss-parser",
"_id": "rss-parser@3.7.3",
"_inBundle": false,
"_integrity": "sha512-Ys+fC5wHqWkfR+jNEL7QvC0crUCAcygxHel3ab9QQzHjmH7fNz379lrp984CuCPUf3WzxHEa73lL86W7yc9qjQ==",
"_location": "/rss-parser",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "rss-parser",
"name": "rss-parser",
"escapedName": "rss-parser",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/rss-parser/-/rss-parser-3.7.3.tgz",
"_shasum": "961cd155ca26cb2ba8f52aa7f2321099b8b5ecf0",
"_spec": "rss-parser",
"_where": "C:\\Users\\thequ\\Documents\\Mynij\\Mynij-unit-tests",
"author": {
"name": "Bobby Brennan"
},
"bugs": {
"url": "https://github.com/bobby-brennan/rss-parser/issues"
},
"bundleDependencies": false,
"dependencies": {
"entities": "^1.1.1",
"xml2js": "^0.4.19"
},
"deprecated": false,
"description": "A lightweight RSS parser, for Node and the browser",
"devDependencies": {
"@types/xml2js": "^0.4.3",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-preset-env": "^1.7.0",
"chai": "^3.4.1",
"express": "^4.16.3",
"mocha": "^5.2.0",
"puppeteer": "^1.16.0",
"webpack": "^4.41.0",
"webpack-cli": "^3.3.9"
},
"directories": {
"test": "test"
},
"homepage": "https://github.com/bobby-brennan/rss-parser#readme",
"keywords": [
"RSS",
"RSS to JSON",
"RSS reader",
"RSS parser",
"RSS to JS",
"Feed reader"
],
"license": "MIT",
"main": "index.js",
"name": "rss-parser",
"repository": {
"type": "git",
"url": "git+https://github.com/bobby-brennan/rss-parser.git"
},
"scripts": {
"build": "./scripts/build.sh",
"test": "mocha --exit"
},
"types": "index.d.ts",
"version": "3.7.3"
}
set -e
webpack-cli --mode=development --target=web
webpack-cli --mode=production --target=web --output-filename=dist/[name].min.js --profile --json > dist/stats.json
"use strict";
const express = require('express');
const expect = require('chai').expect;
const Browser = require('puppeteer');
let browser = null;
let page = null;
const PORT = 3333;
const PARSE_TIMEOUT = 1000;
describe('Browser', function() {
if (process.env.SKIP_BROWSER_TESTS) {
console.log('skipping browser tests');
return;
}
before(function(done) {
this.timeout(5000);
let app = express();
app.use(express.static(__dirname));
app.use('/dist', express.static(__dirname + '/../dist'));
app.listen(PORT, err => {
if (err) return done(err);
Browser.launch({args: ['--no-sandbox']})
.then(b => browser = b)
.then(_ => browser.newPage())
.then(p => {
page = p;
return page.goto('http://localhost:3333/index.html');
})
.then(_ => done())
.catch(e => done(e));
});
});
after(() => browser.close());
it('should have window.RSSParser', () => {
return page.evaluate(() => {
return typeof window.RSSParser;
}).then(type => {
expect(type).to.equal('function');
})
});
it('should parse reddit', function() {
this.timeout(PARSE_TIMEOUT + 1000);
return page.evaluate(() => {
var parser = new RSSParser();
parser.parseURL('http://localhost:3333/input/reddit.rss', function(err, data) {
window.error = err;
window.reddit = data;
})
})
.then(_ => {
return new Promise(resolve => setTimeout(resolve, PARSE_TIMEOUT))
})
.then(_ => page.evaluate(() => {
return window.error;
}))
.then(err => {
expect(err).to.equal(null);
})
.then(_ => page.evaluate(() => {
return window.reddit.title;
}))
.then(title => {
expect(title).to.equal('reddit: the front page of the internet');
})
})
})
<script src="dist/rss-parser.min.js"></script>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="en-US">
<id>tag:github.com,2008:https://github.com/gulpjs/gulp/releases</id>
<link type="text/html" rel="alternate" href="https://github.com/gulpjs/gulp/releases"/>
<link type="application/atom+xml" rel="self" href="https://github.com/gulpjs/gulp/releases.atom"/>
<title>Release notes from gulp</title>
<updated>2015-06-01T23:49:41+02:00</updated>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.9.0</id>
<updated>2015-06-01T23:49:41+02:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.9.0"/>
<title>v3.9.0</title>
<content type="html">&lt;p&gt;3.9.0&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.11</id>
<updated>2015-02-09T21:37:28+01:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.11"/>
<title>v3.8.11</title>
<content type="html">&lt;p&gt;3.8.11&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.10</id>
<updated>2014-11-04T01:11:30+01:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.10"/>
<title>v3.8.10</title>
<content type="html">&lt;p&gt;3.8.10&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.9</id>
<updated>2014-10-22T08:55:20+02:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.9"/>
<title>v3.8.9</title>
<content type="html">&lt;p&gt;3.8.9&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.8</id>
<updated>2014-09-07T22:20:11+02:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.8"/>
<title>v3.8.8</title>
<content type="html">&lt;p&gt;3.8.8&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.7</id>
<updated>2014-08-02T06:58:19+02:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.7"/>
<title>v3.8.7</title>
<content type="html">&lt;p&gt;3.8.7&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.6</id>
<updated>2014-07-10T00:06:50+02:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.6"/>
<title>v3.8.6</title>
<content type="html">&lt;p&gt;3.8.6&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.5</id>
<updated>2014-06-27T08:53:54+02:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.5"/>
<title>v3.8.5</title>
<content type="html">&lt;p&gt;3.8.5&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.4</id>
<updated>2014-06-27T08:38:43+02:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.4"/>
<title>v3.8.4</title>
<content type="html">&lt;p&gt;3.8.4&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.8.3</id>
<updated>2014-06-26T23:17:51+02:00</updated>
<link rel="alternate" type="text/html" href="/gulpjs/gulp/releases/tag/v3.8.3"/>
<title>v3.8.3</title>
<content type="html">&lt;p&gt;3.8.3&lt;/p&gt;</content>
<author>
<name>contra</name>
</author>
<media:thumbnail height="30" width="30" url="https://avatars3.githubusercontent.com/u/425716?v=3&amp;s=60"/>
</entry>
</feed>
This diff is collapsed.
<?xml version="1.0" encoding="ISO-8859-1" ?>
<rss version="0.92">
<channel>
<title>RSS0.92 Example</title>
<link>http://www.oreilly.com/example/index.html</link>
<description>This is an example RSS0.91 feed</description>
<language>en-gb</language>
<copyright>Copyright 2002, Oreilly and Associates.</copyright>
<managingEditor>editor@oreilly.com</managingEditor>
<webMaster>webmaster@oreilly.com</webMaster>
<rating>5</rating>
<pubDate>03 Apr 02 1500 GMT</pubDate>
<lastBuildDate>03 Apr 02 1500 GMT</lastBuildDate>
<item>
<title>The First Item</title>
<link>http://www.oreilly.com/example/001.html</link>
<description>This is the first item.</description>
<source url="http://www.anothersite.com/index.xml">Another Site</source>
<enclosure url="http://www.oreilly.com/001.mp3" length="54321" type="audio/mpeg"/>
<category domain="http://www.dmoz.org">Business/Industries/Publishing/Publishers/Nonfiction/</category>
</item>
<item>
<title>The Second Item</title>
<link>http://www.oreilly.com/example/002.html</link>
<description>This is the second item.</description>
<source url="http://www.anothersite.com/index.xml">Another Site</source>
<enclosure url="http://www.oreilly.com/002.mp3" length="54321" type="audio/mpeg"/>
<category domain="http://www.dmoz.org">Business/Industries/Publishing/Publishers/Nonfiction/</category>
</item>
</channel>
</rss>
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0">
<channel>
<title>Instant Article Test</title>
<link>https://localhost:8000</link>
<description>1, 2, 1, 2… check the mic!</description>
<language>en</language>
<pubDate>Fri, 13 May 2016 15:14:05 GMT</pubDate>
<dc:date>2016-05-13T15:14:05Z</dc:date>
<dc:language>en</dc:language>
<item>
<title>My first Instant Article</title>
<link>https://localhost:8000</link>
<description>Lorem ipsum</description>
<content:encoded>&lt;b&gt;Lorem&lt;/b&gt; ipsum</content:encoded>
<pubDate>Wed, 04 May 2016 06:53:45 GMT</pubDate>
<guid>https://localhost:8000</guid>
<dc:creator>tobi</dc:creator>
<dc:date>2016-05-04T06:53:45Z</dc:date>
</item>
</channel>
</rss>
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content" xmlns:dc="http://purl.org/dc/elements/1.1" xmlns:itunes="http://www.itunes.com/DTDs/Podcast-1.0.dtd" version="2.0">
<channel>
<title>Taverncast - Happy Hour in Your Head - Since 2005</title>
<itunes:author>Bryce Erwin, Bill Ticknor, Michelle O'Neill, Mike Monan, Aric Watson, Jennifer Albrecht, Lauren Hoban and Derek Chew</itunes:author>
<link>http://www.taverncast.com/</link>
<description>Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!</description>
<itunes:subtitle>Beer - Talk - Fun. It's Happy Hour in Your Head!</itunes:subtitle>
<itunes:summary>Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!</itunes:summary>
<language>en-us</language>
<copyright>Taverncast, 2015</copyright>
<managingEditor>taverncast@taverncast.com</managingEditor>
<pubDate>Wed, 03 Aug 2005 18:00:00 GMT</pubDate>
<generator>me mitts</generator>
<itunes:owner>
<itunes:name>Taverncast</itunes:name>
<itunes:email>taverncast@taverncast.com</itunes:email>
</itunes:owner>
<itunes:explicit>no</itunes:explicit>
<image>
<url>http://www.taverncast.com/itunes-logo.png</url>
<title>Taverncast - Happy Hour in Your Head - Since 2005</title>
<link>http://www.taverncast.com/</link>
<width>144</width>
<height>300</height>
</image>
<itunes:link rel="image" type="video/jpeg" href="http://www.taverncast.com/itunes-logo.png">Taverncast</itunes:link>
<itunes:image>href=http://www.taverncast.com/itunes-logo.png"&gt;</itunes:image>
<itunes:category text="Society &amp; Culture" />
<itunes:category text="Games &amp; Hobbies">
<itunes:category text="Hobbies" />
</itunes:category>
<item>
<title>Taverncast 62 - Temporal Anomaly</title>
<link>http://taverncast.com/</link>
<dc:creator>Taverncast: www.taverncast.com</dc:creator>
<content:encoded><![CDATA[Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.]]></content:encoded>
<comments>http://www.taverncast.com</comments>
<author>Taverncast</author>
<itunes:author>Taverncast</itunes:author>
<description>Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.</description>
<itunes:subtitle>Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.</itunes:subtitle>
<itunes:summary>Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.</itunes:summary>
<enclosure url="http://www.podtrac.com/pts/redirect.mp3?http://www.taverncast.com/shows/taverncast-62.mp3" length="47160424" type="audio/mpeg" />
<guid>http://taverncast.com/shows/taverncast-62.mp3</guid>
<pubDate>07 Nov 2015 12:00:00 EST</pubDate>
<category>Society &amp; Culture</category>
<itunes:explicit>no</itunes:explicit>
<itunes:duration>1:09:50</itunes:duration>
<itunes:keywords>Taverncast, beer, games, hobbies, gaming, computer, geek, party, movies, comics, pop-culture, weird, comedy, humor, funny, talk, nerd, news, politics, paranormal, conspiracy, morning show, stone brewing company, time travel, back to the future, past, future, present, timeline, temporal, travel, time, dog, doodle, poodle, apple, laptop, computer, arrogant, bastard, arrogant bastard</itunes:keywords>
</item>
</channel>
</rss>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:cf="http://www.microsoft.com/schemas/rss/core/2005">
<channel>
<title>Transfermarkt</title>
<link>http://www.transfermarkt.de/rss/news</link>
<description><![CDATA[Die 10 letzten News
]]></description>
<image><title>Transfermarkt</title>
<link>http://www.transfermarkt.de</link>
<url>http://www.transfermarkt.de/images/logo.png</url>
</image>
<language>de-de</language>
<pubDate>Mon, 07 Jan 2019 18:12:17 +0100</pubDate>
<atom:link>http://www.transfermarkt.de/rss/news</atom:link>
<item>
<title>
Manager bewertet Transfers | Eberl lobt BVB für Pulisic-Deal – Hudson-Odoi kostet „fast so viel wie mein Kader“</title>
<link>
http://www.transfermarkt.de/eberl-lobt-bvb-fur-pulisic-deal-ndash-hudson-odoi-kostet-bdquo-fast-so-viel-wie-mein-kader-ldquo-/view/news/326649?rss</link>
<pubDate>
Mon, 07 Jan 2019 17:19:19 +0100</pubDate>
<description><![CDATA[
Max Eberl (Foto) hat dem BVB zum Transfererl&ouml;s von Christian Pulisic (20) gratuliert. Gleichzeitig prangerte Gladbachs Sportdirektor allerdings auch derart hohe Abl&ouml;sesummen an....
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/eberl-lobt-bvb-fur-pulisic-deal-ndash-hudson-odoi-kostet-bdquo-fast-so-viel-wie-mein-kader-ldquo-/view/news/326649?rss</guid>
</item>
<item>
<title>
Im zweiten Anlauf | Magdeburg verleiht Talent Harant – Fünfter FCM-Spieler in Halberstadt</title>
<link>
http://www.transfermarkt.de/magdeburg-verleiht-talent-harant-ndash-funfter-fcm-spieler-in-halberstadt/view/news/326660?rss</link>
<pubDate>
Mon, 07 Jan 2019 16:55:00 +0100</pubDate>
<description><![CDATA[
Der 1.FC Magdeburg treibt seine Umstrukturierung des Kaders im Winter-Transferfenster weiter voran. Nachdem Jan Kirchhoff und Timo Perthel den Kader von Trainer Michael Oenning verst&auml;rkten und...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/magdeburg-verleiht-talent-harant-ndash-funfter-fcm-spieler-in-halberstadt/view/news/326660?rss</guid>
</item>
<item>
<title>
Transfer fix | Iborra zurück nach Spanien: Villarreal plant langfristig mit Leicesters Reservisten</title>
<link>
http://www.transfermarkt.de/iborra-zuruck-nach-spanien-villarreal-plant-langfristig-mit-leicesters-reservisten/view/news/326658?rss</link>
<pubDate>
Mon, 07 Jan 2019 16:29:39 +0100</pubDate>
<description><![CDATA[
Der FC Villarreal hat sich mit Leicester City geeinigt und die Dienste von Vicente Iborra (30, Foto) gesichert. Bei den &bdquo;Foxes&ldquo; stand der Mittelfeldspieler seit 2017 unter Vertrag, kam...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/iborra-zuruck-nach-spanien-villarreal-plant-langfristig-mit-leicesters-reservisten/view/news/326658?rss</guid>
</item>
<item>
<title>
Jetzt bei TM.tv | BVB-Interesse an Werner: Philipp im Gegenzug nach Leipzig?</title>
<link>
http://www.transfermarkt.de/bvb-interesse-an-werner-philipp-im-gegenzug-nach-leipzig-/view/news/326652?rss</link>
<pubDate>
Mon, 07 Jan 2019 16:00:00 +0100</pubDate>
<description><![CDATA[
Tauschen Timo Werner und Maximilian Philipp die Klubs? Nach dem &bdquo;Kicker&ldquo; berichtet nun auch die &bdquo;Bild&ldquo; vom Interesse des BVB an Leipzigs Werner (Foto). Zudem wird berichtet,...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/bvb-interesse-an-werner-philipp-im-gegenzug-nach-leipzig-/view/news/326652?rss</guid>
</item>
<item>
<title>
Rechtsverteidiger | Dritter Wintertransfer: Däne Paulsen soll Ingolstadt aus der Misere helfen</title>
<link>
http://www.transfermarkt.de/dritter-wintertransfer-dane-paulsen-soll-ingolstadt-aus-der-misere-helfen/view/news/326654?rss</link>
<pubDate>
Mon, 07 Jan 2019 15:46:15 +0100</pubDate>
<description><![CDATA[
Der FC Ingolstadt hat Bj&ouml;rn Paulsen (Foto) als seinen dritten Neuzugang in diesem Winter pr&auml;sentiert. Der strauchelnde Zweitligist stattete den d&auml;nischen Rechtsverteidiger mit einem...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/dritter-wintertransfer-dane-paulsen-soll-ingolstadt-aus-der-misere-helfen/view/news/326654?rss</guid>
</item>
<item>
<title>
Vertrag mit Option | Baffo darf sich bis Sommer in Duisburg beweisen – „Wir mussten reagieren“</title>
<link>
http://www.transfermarkt.de/baffo-darf-sich-bis-sommer-in-duisburg-beweisen-ndash-bdquo-wir-mussten-reagieren-ldquo-/view/news/326647?rss</link>
<pubDate>
Mon, 07 Jan 2019 15:19:03 +0100</pubDate>
<description><![CDATA[
Zweitligist MSV Duisburg hat den vereinslosen Joseph Baffo (Foto) mit einem Vertrag bis Saisonende ausgestattet. Der 26-j&auml;hrige Innenverteidiger war zuletzt zur Probe im Training der...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/baffo-darf-sich-bis-sommer-in-duisburg-beweisen-ndash-bdquo-wir-mussten-reagieren-ldquo-/view/news/326647?rss</guid>
</item>
<item>
<title>
Nach Haidara-Transfer | Bericht: Salzburg-Führung verhinderte Wolf-Wechsel nach Leipzig – Veto von Rose</title>
<link>
http://www.transfermarkt.de/bericht-salzburg-fuhrung-verhinderte-wolf-wechsel-nach-leipzig-ndash-veto-von-rose/view/news/326640?rss</link>
<pubDate>
Mon, 07 Jan 2019 14:47:00 +0100</pubDate>
<description><![CDATA[
RB Leipzig ist einem Bericht zufolge bei dem Versuch gescheitert, Hannes Wolf (Foto) vom Schwester-Klub RB Salzburg nach Sachsen zu lotsen. Das meldeten die &bdquo;Salzburger Nachrichten&ldquo; mit...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/bericht-salzburg-fuhrung-verhinderte-wolf-wechsel-nach-leipzig-ndash-veto-von-rose/view/news/326640?rss</guid>
</item>
<item>
<title>
Wechsel in die Regionalliga | Regensburg verabschiedet „verdienten Jahn-Profi“ Kopp nach Bayreuth</title>
<link>
http://www.transfermarkt.de/regensburg-verabschiedet-bdquo-verdienten-jahn-profi-ldquo-kopp-nach-bayreuth/view/news/326639?rss</link>
<pubDate>
Mon, 07 Jan 2019 14:15:21 +0100</pubDate>
<description><![CDATA[
Defensiv-Allrounder Sven Kopp (23, Foto) verl&auml;sst den SSV Jahn Regensburg nach viereinhalb Jahren. Der Klub verk&uuml;ndete am Montag, dass der &bdquo;verdiente Jahn-Profi&ldquo; zur SpVgg...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/regensburg-verabschiedet-bdquo-verdienten-jahn-profi-ldquo-kopp-nach-bayreuth/view/news/326639?rss</guid>
</item>
<item>
<title>
Regionalliga West | Zschiesche übernimmt Trainerposten beim Bonner SC</title>
<link>
http://www.transfermarkt.de/zschiesche-ubernimmt-trainerposten-beim-bonner-sc/view/news/326633?rss</link>
<pubDate>
Mon, 07 Jan 2019 14:01:09 +0100</pubDate>
<description><![CDATA[
Markus Zschiesche (Foto) &uuml;bernimmt das Traineramt beim West-Regionalligisten Bonner SC. Der 36-J&auml;hrige stand vergangene Saison f&uuml;r den Berliner AK 07 an der Seitenlinie und...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/zschiesche-ubernimmt-trainerposten-beim-bonner-sc/view/news/326633?rss</guid>
</item>
<item>
<title>
Chelsea plant Angebot | Cagliari erwartet Rekordablöse für Barella: Teuerster Italiener nach Jorginho &amp; Buffon?</title>
<link>
http://www.transfermarkt.de/cagliari-erwartet-rekordablose-fur-barella-teuerster-italiener-nach-jorginho-amp-buffon-/view/news/326629?rss</link>
<pubDate>
Mon, 07 Jan 2019 13:51:00 +0100</pubDate>
<description><![CDATA[
Abseits der italienischen Top-Klubs hat sich in den vergangenen Monaten Nicol&ograve; Barella (Foto) zu einem der Shootingstars der italienischen Serie A entwickelt. Das 21-j&auml;hrige...
]]></description>
<author>
Transfermarkt</author>
<guid isPermaLink="true">
http://www.transfermarkt.de/cagliari-erwartet-rekordablose-fur-barella-teuerster-italiener-nach-jorginho-amp-buffon-/view/news/326629?rss</guid>
</item>
</channel></rss>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:content="http://purl.org/rss/1.0/modules/content" xmlns:dc="http://purl.org/dc/elements/1.1" xmlns:itunes="http://www.itunes.com/DTDs/Podcast-1.0.dtd" version="2.0">
<channel>
<title>Taverncast - Happy Hour in Your Head - Since 2005</title>
<itunes:author>Bryce Erwin, Bill Ticknor, Michelle O'Neill, Mike Monan, Aric Watson, Jennifer Albrecht, Lauren Hoban and Derek Chew</itunes:author>
<link>http://www.taverncast.com/</link>
<description>Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!</description>
<itunes:subtitle>Beer - Talk - Fun. It's Happy Hour in Your Head!</itunes:subtitle>
<itunes:summary>Order a beer, pull up a seat and join your friends at Taverncast for our patented un-scripted roundtable discussions, covering a wide range of topics with a unique wit and inviting style - all blended together, it's Happy Hour in Your Head!</itunes:summary>
<language>en-us</language>
<copyright>Taverncast, 2015</copyright>
<managingEditor>taverncast@taverncast.com</managingEditor>
<pubDate>Wed, 03 Aug 2005 18:00:00 GMT</pubDate>
<generator>me mitts</generator>
<itunes:owner>
<itunes:name>Taverncast</itunes:name>
<itunes:email>taverncast@taverncast.com</itunes:email>
</itunes:owner>
<itunes:explicit>no</itunes:explicit>
<image>
<url>http://www.taverncast.com/itunes-logo.png</url>
<title>Taverncast - Happy Hour in Your Head - Since 2005</title>
<link>http://www.taverncast.com/</link>
<width>144</width>
<height>300</height>
</image>
<itunes:link rel="image" type="video/jpeg" href="http://www.taverncast.com/itunes-logo.png">Taverncast</itunes:link>
<itunes:image>href=http://www.taverncast.com/itunes-logo.png"&gt;</itunes:image>
<itunes:keywords>Culture,Society</itunes:keywords>
<item>
<title>Taverncast 62 - Temporal Anomaly</title>
<link>http://taverncast.com/</link>
<dc:creator>Taverncast: www.taverncast.com</dc:creator>
<content:encoded><![CDATA[Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.]]></content:encoded>
<comments>http://www.taverncast.com</comments>
<author>Taverncast</author>
<itunes:author>Taverncast</itunes:author>
<description>Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.</description>
<itunes:subtitle>Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.</itunes:subtitle>
<itunes:summary>Taverncast tosses around the age old topic: time travel! Where would you go? Why would you go? Also, Bryce has customer service issues, and Mike buys a dog.</itunes:summary>
<enclosure url="http://www.podtrac.com/pts/redirect.mp3?http://www.taverncast.com/shows/taverncast-62.mp3" length="47160424" type="audio/mpeg" />
<guid>http://taverncast.com/shows/taverncast-62.mp3</guid>
<pubDate>07 Nov 2015 12:00:00 EST</pubDate>
<category>Society &amp; Culture</category>
<itunes:explicit>no</itunes:explicit>
<itunes:duration>1:09:50</itunes:duration>
<itunes:keywords>Taverncast, beer, games, hobbies, gaming, computer, geek, party, movies, comics, pop-culture, weird, comedy, humor, funny, talk, nerd, news, politics, paranormal, conspiracy, morning show, stone brewing company, time travel, back to the future, past, future, present, timeline, temporal, travel, time, dog, doodle, poodle, apple, laptop, computer, arrogant, bastard, arrogant bastard</itunes:keywords>
</item>
</channel>
</rss>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" xml:lang="en-US">
<entry>
<id>tag:github.com,2008:Repository/11167738/v3.9.0</id>
</entry>
</feed>
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0">
<channel>
<title>foobar on Narro</title>
<link>http://on.narro.co/f</link>
<description>foobar uses Narro to create a podcast of articles transcribed to audio.</description>
<language>en</language>
<copyright>All article content copyright of respective source authors.</copyright>
<managingEditor>foobar@gmail.com</managingEditor>
<webMaster>josh@narro.co</webMaster>
<pubDate>Fri, 08 Jul 2016 13:40:00 UTC</pubDate>
<generator>gopod - http://github.com/jbckmn/gopod</generator>
<ttl>20</ttl>
<itunes:author>foobar@gmail.com</itunes:author>
<itunes:subtitle>foobar uses Narro to create a podcast of articles transcribed to audio.</itunes:subtitle>
<itunes:summary>foobar uses Narro to create a podcast of articles transcribed to audio.</itunes:summary>
<itunes:explicit>no</itunes:explicit>
<itunes:owner>
<itunes:name>foobar</itunes:name>
<itunes:email>foobar@gmail.com</itunes:email>
</itunes:owner>
<itunes:image href="https://www.narro.co/images/narro-icon-lg.png"></itunes:image>
<atom:link href="http://on.narro.co/f" rel="self" type="application/rss+xml"></atom:link>
<item>
<link>https://www.narro.co/article/54e703933058540300000069</link>
<description>Listen to your reading list anywhere. Narro will take your bookmarked articles and read them back to you as a podcast.&lt;br/&gt; http://www.narro.co/faq&lt;br/&gt; &lt;ul class=&#34;linkList&#34;&gt;&lt;/ul&gt;</description>
<title>FAQ for Narro</title>
<pubDate>Fri, 20 Feb 2015 09:51:15 UTC</pubDate>
<author>foobar@gmail.com</author>
<guid>https://www.narro.co/article/54e703933058540300000069</guid>
<itunes:author>foobar@gmail.com</itunes:author>
<itunes:subtitle>FAQ for Narro</itunes:subtitle>
<itunes:summary>Listen to your reading list anywhere. Narro will take your bookmarked articles and read them back to you as a podcast. ... http://www.narro.co/faq ... &lt;ul class=&#34;linkList&#34;&gt;&lt;/ul&gt;</itunes:summary>
<itunes:explicit>no</itunes:explicit>
<itunes:duration>74</itunes:duration>
<itunes:image href="http://example.com/someImage.jpg"/>
<enclosure url="https://s3.amazonaws.com/nareta-articles/audio/54d046c293f79c0300000003/7e2d2b00-a945-441a-f49b-063786a319a4.mp3" length="74" type="audio/mpeg"></enclosure>
</item>
</channel>
</rss>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
<head><title>Document Moved</title></head>
<body><h1>Object Moved</h1>This document may be found <a HREF="http://fishhawk2.marketwatch.com/rss/pf/">here</a></body>
\ No newline at end of file
<rss>
<channel>
<title>UOL Noticias</title>
<link>http://noticias.uol.com.br/</link>
<description>Últimas Notícias</description>
<language></language>
<category>Notícias</category>
<copyright></copyright>
<image>
<title>UOL Noticias - Últimas Notícias</title>
<url>http://rss.i.uol.com.br/uol_rss.gif</url>
<link>http://noticias.uol.com.br/ultimas/</link>
</image>
<item>
<title><![CDATA[Ibope: Bolsonaro perde de Haddad, Ciro e Alckmin em simulações de 2º turno]]></title>
<link><![CDATA[https://noticias.uol.com.br/politica/eleicoes/2018/noticias/2018/09/24/ibope-bolsonaro-perde-de-haddad-ciro-e-alckmin-em-simulacoes-de-2-turno.htm]]></link>
<description><![CDATA[<img src='https://conteudo.imguol.com.br/c/noticias/5f/2018/09/17/presidenciaveis-1537211917356_142x100.jpg' align="left" /> Líder na pesquisa Ibope de intenção de voto para o primeiro turno divulgada nesta segunda-feira (24), o candidato do PSL a presidente, Jair Bolsonaro, não tem o mesmo desempenho nas simulações de segundo turno feitas pela empresa no mesmo levantamento. ]]></description>
<pubDate>Seg, 24 Set 2018 19:42:40 -0300</pubDate>
</item>
<item>
<title><![CDATA[Promotoria abre inquérito para apurar suspeita de improbidade de Skaf no Sebrae-SP]]></title>
<link><![CDATA[https://www1.folha.uol.com.br/poder/2018/09/promotoria-abre-inquerito-para-apurar-suspeita-de-improbidade-de-skaf-no-sebrae-sp.shtml]]></link>
<description></description>
<pubDate>Seg, 24 Set 2018 19:38:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Apucarana garante continuidade do Pré Aprendiz; ano vai fechar com 13 mil pessoas qualificadas]]></title>
<link><![CDATA[https://tnonline.uol.com.br/noticias/apucarana/45,470927,24,09,apucarana-garante-continuidade-do-pre-aprendiz-ano-vai-fechar-com-13-mil-pessoas-qualificadas]]></link>
<description><![CDATA[<img src='https://m1.tnonline.com.br/entre/2018/09/24/tn_4017f330b2_preapre.jpg' align="left" /> Programa Pré Aprendiz, que é realizado pela Secretaria Municipal de Assistência Social de Apucarana em parceria com o Serviço Social do Comércio (Sesc), terá continuidade em 2019. O termo de renovaç... ]]></description>
<pubDate>Seg, 24 Set 2018 19:35:07 -0300</pubDate>
</item>
<item>
<title><![CDATA[Dow Jones fecha em baixa de 0,68%]]></title>
<link><![CDATA[https://economia.uol.com.br/noticias/efe/2018/09/24/dow-jones-fecha-em-baixa-de-068.htm]]></link>
<description><![CDATA[ Nova York, 24 set (EFE).- O índice Dow Jones Industrial fechou nesta segunda-feira em baixa de 0,68% em mais um pregão marcado pela disputa comercial entre Estados Unidos e China e por especulações sobre a possível renúncia de Rod Rosenstein ao cargo de procurador-geral adjunto dos Estados Unidos. ]]></description>
<pubDate>Seg, 24 Set 2018 19:32:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Rival de Putin é condenado a mais 20 dias de prisão]]></title>
<link><![CDATA[https://noticias.uol.com.br/ultimas-noticias/ansa/2018/09/24/rival-de-putin-e-condenado-a-mais-20-dias-de-prisao.htm]]></link>
<description><![CDATA[ MOSCOU, 24 SET (ANSA) - O líder de oposição russo Alexei Navalny foi condenado na noite desta segunda-feira (24) a mais 20 dias de prisão por organizar manifestações não autorizadas contra o governo de Vladimir Putin, informou a mídia russa. Navalny foi preso nesta manhã pouco tempo depois de ser libertado da cadeia após cumprir outra sentença de 30 dias.    ]]></description>
<pubDate>Seg, 24 Set 2018 19:32:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Em entrevista, Bolsonaro chama proposta de Paulo Guedes para imposto de renda de "ousada"]]></title>
<link><![CDATA[https://noticias.uol.com.br/ultimas-noticias/reuters/2018/09/24/em-entrevista-bolsonaro-chama-proposta-de-paulo-guedes-para-imposto-de-renda-de-ousada.htm]]></link>
<description><![CDATA[ BRASÍLIA (Reuters) - O candidato do PSL à Presidência, Jair Bolsonaro, classificou nesta segunda-feira a proposta do seu principal assessor econômico, Paulo Guedes, para a mudança na forma de cobrança do imposto de renda para pessoa física de "ousada". ]]></description>
<pubDate>Seg, 24 Set 2018 19:30:42 -0300</pubDate>
</item>
<item>
<title><![CDATA[Relatório do governo dos EUA acusa militares de Mianmar de atrocidades contra muçulmanos rohingyas]]></title>
<link><![CDATA[https://noticias.uol.com.br/ultimas-noticias/reuters/2018/09/24/relatorio-do-governo-dos-eua-acusa-militares-de-mianmar-de-atrocidades-contra-muculmanos-rohingyas.htm]]></link>
<description><![CDATA[ Por Matt Spetalnick e Jason Szep ]]></description>
<pubDate>Seg, 24 Set 2018 19:29:18 -0300</pubDate>
</item>
<item>
<title><![CDATA[Enfermeira de Chávez alega 'perseguição' em Corte espanhola]]></title>
<link><![CDATA[https://noticias.uol.com.br/ultimas-noticias/ansa/2018/09/24/enfermeira-de-chavez-alega-perseguicao-em-corte-espanhola.htm]]></link>
<description><![CDATA[ MADRI, 24 SET (ANSA) - A ex-enfermeira do falecido Hugo Chávez, ex-presidente venezuelano, prestou depoimento hoje (24) na Corte Nacional da Espanha e alegou sofrer perseguiç... ]]></description>
<pubDate>Seg, 24 Set 2018 19:25:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Match Eleitoral já soma mais de 500 mil testes completos; ache seu candidato]]></title>
<link><![CDATA[https://www1.folha.uol.com.br/poder/2018/09/match-eleitoral-ja-soma-mais-de-500-mil-testes-completos-ache-seu-candidato.shtml]]></link>
<description></description>
<pubDate>Seg, 24 Set 2018 19:22:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Match Eleitoral já soma mais de 500 mil testes completos; ache seu candidato]]></title>
<link><![CDATA[https://redir.folha.com.br/redir/online/poder/eleicoes-2018/rss091/*https://www1.folha.uol.com.br/poder/2018/09/match-eleitoral-ja-soma-mais-de-500-mil-testes-completos-ache-seu-candidato.shtml]]></link>
<description><![CDATA[ Já são mais de 500 mil testes completos no Match Eleitoral, ferramenta criada pela Folha e pelo Datafolha para ajudar o eleitor a escolher seu deputado federal por São Paulo, Minas Gerais e Rio de Janeiro, além de senadores por São Paulo. Leia mais (09/24/2018 - 19h22) ]]></description>
<pubDate>Seg, 24 Set 2018 19:22:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Acordo adicional da Argentina com FMI está "perto de acontecer", diz Macri]]></title>
<link><![CDATA[https://www1.folha.uol.com.br/mercado/2018/09/acordo-adicional-da-argentina-com-fmi-esta-perto-de-acontecer-diz-macri.shtml]]></link>
<description></description>
<pubDate>Seg, 24 Set 2018 19:22:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Fernanda Melchionna: a Primavera Feminista vai derrotar Bolsonaro nas ruas e nas urnas!]]></title>
<link><![CDATA[https://agoraequesaoelas.blogfolha.uol.com.br/?p=1609]]></link>
<description></description>
<pubDate>Seg, 24 Set 2018 19:21:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Ibope: Bolsonaro estaciona na liderança; Haddad segue avançando]]></title>
<link><![CDATA[https://congressoemfoco.uol.com.br/eleicoes/ibope-bolsonaro-estaciona-na-lideranca-haddad-segue-avancando/]]></link>
<description><![CDATA[<img src='https://static.congressoemfoco.uol.com.br/2018/09/bolsonaro-haddad.jpg' align="left" /> Em nova pesquisa Ibope divulgada nesta segunda-feira (24), o candidato do PSL à Presidência, Jair Bolsonaro, continua líder com 28% das intenções de voto, sem apresentar crescimento em relação à última pesquisa, quando teve a mesma pontuação. O candidato do PT, Fernando Haddad, aproxima-se de Bolsonaro com 22%, uma diferença de 3 pontos percentuais em [?] ]]></description>
<pubDate>Seg, 24 Set 2018 19:20:08 -0300</pubDate>
</item>
<item>
<title><![CDATA[Assange chegou a renunciar a asilo do Equador, segundo carta privada]]></title>
<link><![CDATA[https://noticias.uol.com.br/ultimas-noticias/afp/2018/09/24/assange-chegou-a-renunciar-a-asilo-do-equador-segundo-carta-privada.htm]]></link>
<description><![CDATA[ Quito, 24 Set 2018 (AFP) - O fundador do WikiLeaks, Julian Assange, refugiado na embaixada equatoriana em Londres há seis anos, chegou a renunciar ao asilo concedido por Quito, segundo uma carta assinada por ele em dezembro passado à qual a AFP teve acesso. ]]></description>
<pubDate>Seg, 24 Set 2018 19:20:00 -0300</pubDate>
</item>
<item>
<title><![CDATA[Fama "A" é campeã da Primeira Divisão da Copa Cidade Alta de Futebol Suíço]]></title>
<link><![CDATA[https://tnonline.uol.com.br/noticias/apucarana/45,470926,24,09,fama-a-e-campea-da-primeira-divisao-da-copa-cidade-alta-de-futebol-suico]]></link>
<description><![CDATA[<img src='https://m1.tnonline.com.br/entre/2018/09/24/tn_563c48a591_fama-ajpg.jpg' align="left" /> om a Arena Fama lotada, a equipe da Molas Fama/Multividros "A" foi campeã neste sábado da Primeira Divisão da 10ª edição da Copa Cidade Alta de Futebol Suíço, categoria livre, ao vencer a Estamparia ... ]]></description>
<pubDate>Seg, 24 Set 2018 19:18:49 -0300</pubDate>
</item>
</channel>
</rss>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"feed": {
"items": [
{
"creator": "tobi",
"date": "2016-05-04T06:53:45Z",
"title": "My first Instant Article",
"link": "https://localhost:8000",
"pubDate": "Wed, 04 May 2016 06:53:45 GMT",
"content:encoded": "<b>Lorem</b> ipsum",
"dc:creator": "tobi",
"dc:date": "2016-05-04T06:53:45Z",
"content": "Lorem ipsum",
"contentSnippet": "Lorem ipsum",
"guid": "https://localhost:8000",
"isoDate": "2016-05-04T06:53:45.000Z"
}
],
"title": "Instant Article Test",
"description": "1, 2, 1, 2… check the mic!",
"pubDate": "Fri, 13 May 2016 15:14:05 GMT",
"link": "https://localhost:8000",
"language": "en"
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
{
"feed": {
"items": [
{
"id": "tag:github.com,2008:Repository/11167738/v3.9.0"
}
]
}
}
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment