From cc8b5e4e5f4d865df2f58805cbe19ec06e21c195 Mon Sep 17 00:00:00 2001 From: ManeraKai Date: Sun, 12 Jun 2022 18:51:47 +0300 Subject: Added libremdb #230 --- src/assets/images/imdb.svg | 57 ++++++++++++ src/assets/javascripts/general.js | 1 + src/assets/javascripts/imdb.js | 159 ++++++++++++++++++++++++++++++++++ src/assets/javascripts/utils.js | 4 + src/instances/data.json | 33 ++++--- src/instances/get_instances.py | 24 +++++ src/pages/background/background.js | 4 + src/pages/options/index.html | 79 +++++++++++++++++ src/pages/options/index.pug | 1 + src/pages/options/widgets/general.js | 2 + src/pages/options/widgets/general.pug | 6 ++ src/pages/options/widgets/imdb.js | 42 +++++++++ src/pages/options/widgets/imdb.pug | 26 ++++++ src/pages/popup/popup.html | 4 + src/pages/popup/popup.pug | 6 ++ src/pages/widgets/links.pug | 4 + 16 files changed, 441 insertions(+), 11 deletions(-) create mode 100644 src/assets/images/imdb.svg create mode 100644 src/assets/javascripts/imdb.js create mode 100644 src/pages/options/widgets/imdb.js create mode 100644 src/pages/options/widgets/imdb.pug (limited to 'src') diff --git a/src/assets/images/imdb.svg b/src/assets/images/imdb.svg new file mode 100644 index 00000000..a3f4103c --- /dev/null +++ b/src/assets/images/imdb.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + diff --git a/src/assets/javascripts/general.js b/src/assets/javascripts/general.js index 50d6ee33..4e8b7a7d 100644 --- a/src/assets/javascripts/general.js +++ b/src/assets/javascripts/general.js @@ -64,6 +64,7 @@ const allPopupFrontends = [ "wikipedia", "medium", "quora", + "imdb", "reuters", "peertube", "lbry", diff --git a/src/assets/javascripts/imdb.js b/src/assets/javascripts/imdb.js new file mode 100644 index 00000000..9b0efe42 --- /dev/null +++ b/src/assets/javascripts/imdb.js @@ -0,0 +1,159 @@ +window.browser = window.browser || window.chrome; + +import utils from './utils.js' + +const targets = [ + /^https?:\/{2}(www\.|)imdb\.com.*/ +]; + +let redirects = { + "libremdb": { + "normal": [], + "tor": [] + } +} +function setRedirects(val) { + browser.storage.local.get('cloudflareBlackList', r => { + redirects.libremdb = val; + libremdbNormalRedirectsChecks = [...redirects.libremdb.normal]; + for (const instance of r.cloudflareBlackList) { + const a = libremdbNormalRedirectsChecks.indexOf(instance); + if (a > -1) libremdbNormalRedirectsChecks.splice(a, 1); + } + browser.storage.local.set({ + imdbRedirects: redirects, + libremdbNormalRedirectsChecks + }) + }) +} + +let + disableImdb, + imdbProtocol, + imdbRedirects, + libremdbNormalRedirectsChecks, + libremdbNormalCustomRedirects, + libremdbTorRedirectsChecks, + libremdbTorCustomRedirects; + +function init() { + return new Promise(async resolve => { + browser.storage.local.get( + [ + "disableImdb", + "imdbProtocol", + "imdbRedirects", + "libremdbNormalRedirectsChecks", + "libremdbNormalCustomRedirects", + "libremdbTorRedirectsChecks", + "libremdbTorCustomRedirects", + ], + r => { + disableImdb = r.disableImdb; + imdbProtocol = r.imdbProtocol; + imdbRedirects = r.imdbRedirects; + libremdbNormalRedirectsChecks = r.libremdbNormalRedirectsChecks; + libremdbNormalCustomRedirects = r.libremdbNormalCustomRedirects; + libremdbTorRedirectsChecks = r.libremdbTorRedirectsChecks; + libremdbTorCustomRedirects = r.libremdbTorCustomRedirects; + resolve(); + } + ) + }) +} + +init(); +browser.storage.onChanged.addListener(init) + +function redirect(url, type, initiator) { + if (disableImdb) return; + if (url.pathname == "/") return; + if (type != "main_frame") return; + const all = [ + ...imdbRedirects.libremdb.normal, + ...libremdbNormalCustomRedirects + ]; + if (initiator && (all.includes(initiator.origin) || targets.includes(initiator.host))) return; + if (!targets.some(rx => rx.test(url.href))) return; + + let instancesList; + if (imdbProtocol == 'normal') instancesList = [...libremdbNormalRedirectsChecks, ...libremdbNormalCustomRedirects]; + if (imdbProtocol == 'tor') instancesList = [...libremdbTorRedirectsChecks, ...libremdbTorCustomRedirects]; + if (instancesList.length === 0) return; + + const randomInstance = utils.getRandomInstance(instancesList); + return `${randomInstance}${url.pathname}`; +} + +function reverse(url) { + return new Promise(async resolve => { + await init(); + let protocolHost = utils.protocolHost(url); + const all = [ + ...imdbRedirects.libremdb.normal, + ...imdbRedirects.libremdb.tor, + ...libremdbNormalCustomRedirects, + ...libremdbTorCustomRedirects + ]; + if (!all.includes(protocolHost)) { resolve(); return; } + + resolve(`https://imdb.com${url.pathname}${url.search}`); + }) +} + +function switchInstance(url) { + return new Promise(async resolve => { + await init(); + let protocolHost = utils.protocolHost(url); + const all = [ + ...imdbRedirects.libremdb.tor, + ...imdbRedirects.libremdb.normal, + + ...libremdbNormalCustomRedirects, + ...libremdbTorCustomRedirects, + ]; + if (!all.includes(protocolHost)) { resolve(); return; } + + let instancesList; + if (imdbProtocol == 'normal') instancesList = [...libremdbNormalCustomRedirects, ...libremdbNormalRedirectsChecks]; + else if (imdbProtocol == 'tor') instancesList = [...libremdbTorCustomRedirects, ...libremdbTorRedirectsChecks]; + + const i = instancesList.indexOf(protocolHost); + if (i > -1) instancesList.splice(i, 1); + if (instancesList.length === 0) { resolve(); return; } + + const randomInstance = utils.getRandomInstance(instancesList); + resolve(`${randomInstance}${url.pathname}${url.search}`); + }) +} + +function initDefaults() { + return new Promise(async resolve => { + fetch('/instances/data.json').then(response => response.text()).then(async data => { + let dataJson = JSON.parse(data); + redirects.libremdb = dataJson.libremdb; + browser.storage.local.set({ + disableImdb: false, + imdbProtocol: "normal", + + imdbRedirects: redirects, + + libremdbNormalRedirectsChecks: [...redirects.libremdb.normal], + libremdbNormalCustomRedirects: [], + + libremdbTorRedirectsChecks: [...redirects.libremdb.tor], + libremdbTorCustomRedirects: [], + }, () => resolve()); + }); + }) +} + +export default { + setRedirects, + + redirect, + reverse, + switchInstance, + + initDefaults +}; diff --git a/src/assets/javascripts/utils.js b/src/assets/javascripts/utils.js index 53a5bb2e..0b8bfe41 100644 --- a/src/assets/javascripts/utils.js +++ b/src/assets/javascripts/utils.js @@ -12,6 +12,7 @@ import lbryHelper from "./lbry.js"; import sendTargetsHelper from "./sendTargets.js"; import tiktokHelper from "./tiktok.js"; import quoraHelper from "./quora.js" +import libremdbHelper from "./imdb.js"; import imgurHelper from "./imgur.js"; import localise from './localise.js' @@ -49,6 +50,7 @@ function updateInstances() { wikipediaHelper.setRedirects(instances.wikiless); mediumHelper.setRedirects(instances.scribe); quoraHelper.setRedirects(instances.query); + libremdbHelper.setRedirects(instances.libremdb); sendTargetsHelper.setRedirects(instances.send); tiktokHelper.setRedirects(instances.proxiTok); @@ -366,6 +368,7 @@ function copyRaw(test, copyRawElement) { if (!newUrl) newUrl = await instagramHelper.reverse(url); if (!newUrl) newUrl = await tiktokHelper.reverse(url); if (!newUrl) newUrl = await quoraHelper.reverse(url); + if (!newUrl) newUrl = await libremdbHelper.reverse(url); if (!newUrl) newUrl = await imgurHelper.reverse(url); if (newUrl) { @@ -439,6 +442,7 @@ function switchInstance(test) { if (!newUrl) newUrl = await translateHelper.switchInstance(url); if (!newUrl) newUrl = await mediumHelper.switchInstance(url); if (!newUrl) newUrl = await quoraHelper.switchInstance(url); + if (!newUrl) newUrl = await libremdbHelper.switchInstance(url); if (!newUrl) newUrl = await tiktokHelper.switchInstance(url); if (!newUrl) newUrl = await sendTargetsHelper.switchInstance(url); if (!newUrl) newUrl = await peertubeHelper.switchInstance(url); diff --git a/src/instances/data.json b/src/instances/data.json index 07a11e8a..c0ca20e2 100644 --- a/src/instances/data.json +++ b/src/instances/data.json @@ -10,14 +10,14 @@ "https://tube.cthd.icu", "https://yt.artemislena.eu", "https://invidious.flokinet.to", - "https://invidious.lunar.icu", "https://invidious.tiekoetter.com", + "https://invidious.lunar.icu", "https://invidious.sethforprivacy.com", "https://inv.bp.projectsegfau.lt", "https://invidious.projectsegfau.lt", "https://invidious.weblibre.org", - "https://youtube.076.ne.jp", "https://invidious.esmailelbob.xyz", + "https://youtube.076.ne.jp", "https://invidious.namazso.eu", "https://invidious.kavin.rocks" ], @@ -233,7 +233,9 @@ "https://reddit.beparanoid.de", "https://libreddit.dcs0.hu", "https://reddit.dr460nf1r3.org", - "https://rd.jae.su" + "https://rd.jae.su", + "https://libreddit.mha.fi", + "https://libreddit.foss.wtf" ], "tor": [ "http://spjmllawtheisznfs7uryhxumin26ssv2draj7oope3ok3wuhy43eoyd.onion", @@ -250,7 +252,8 @@ "http://lbrdtjaj7567ptdd4rv74lv27qhxfkraabnyphgcvptl64ijx2tijwid.onion", "http://libreddit.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd.onion", "http://reddit.prnoid54e44a4bduq5due64jkk7wcnkxcp5kv3juncm7veptjcqudgyd.onion", - "http://inz6tbezfwzexva6dize4cqraj2tjdhygxabmcgysccesvw2pybzhbyd.onion" + "http://inz6tbezfwzexva6dize4cqraj2tjdhygxabmcgysccesvw2pybzhbyd.onion", + "http://libreddit.micohauwkjbyw5meacrb4ipicwvwg4xtzl7y7viv53kig2mdcsvwkyyd.onion" ] }, "teddit": { @@ -324,6 +327,13 @@ ], "tor": [] }, + "libremdb": { + "normal": [ + "https://libremdb.herokuapp.com", + "https://libremdb.pussthecat.org" + ], + "tor": [] + }, "simplyTranslate": { "normal": [ "https://simplytranslate.org", @@ -388,6 +398,7 @@ "https://search.snopyta.org", "https://search.stinpriza.org", "https://search.trom.tf", + "https://search.zzls.xyz", "https://searx.bissisoft.com", "https://searx.divided-by-zero.eu", "https://searx.dresden.network", @@ -408,7 +419,6 @@ "https://searx.run", "https://searx.semipvt.com", "https://searx.sethforprivacy.com", - "https://searx.sev.monster", "https://searx.solusar.de", "https://searx.sp-codes.de", "https://searx.stuehieyr.com", @@ -459,9 +469,9 @@ "https://search.roombob.cat", "https://search.sapti.me", "https://search.vojkovic.xyz", - "https://search.zzls.xyz", "https://searx.albony.xyz", "https://searx.be", + "https://searx.becomesovran.com", "https://searx.ebnar.xyz", "https://searx.esmailelbob.xyz", "https://searx.fmac.xyz", @@ -473,6 +483,7 @@ "https://searx.ppeb.me", "https://searx.priv.pw", "https://searx.prvcy.eu", + "https://searx.sev.monster", "https://searx.tiekoetter.com", "https://searxng.ir", "https://searxng.zackptg5.com", @@ -525,6 +536,8 @@ ] }, "peertube": [ + "https://socpeertube.ru", + "https://seka.pona.la", "https://syop.tv", "https://watch.thelema.social", "https://tube.miegl.cz", @@ -639,7 +652,6 @@ "https://video.pthreat.co", "https://peertube.egroc.de", "https://pt.k2s.sk", - "https://gameplay.wtf", "https://video.cats-home.net", "https://peertube.narrativerry.xyz", "https://tube.9minuti.it", @@ -797,7 +809,6 @@ "https://film.node9.org", "https://peertube.mi-site.net", "https://tube.nowtech.io", - "https://videos.mattwilson.org", "https://raptube.antipub.org", "https://video.cm-en-transition.fr", "https://tube.koweb.fr", @@ -906,7 +917,6 @@ "https://video.interru.io", "https://tube.cnr.it", "https://peertube.dtmf.ca", - "https://peertube.dk", "https://tube.ponsonaille.fr", "https://peertube.ares.bioxis-server.fr", "https://tube.int5.net", @@ -1301,7 +1311,6 @@ "https://p.lu", "https://serv3.wiki-tube.de", "https://serv1.wiki-tube.de", - "https://tube.traydent.info", "https://video.lavolte.net", "https://peertube.public.cat", "https://peertube.anduin.net", @@ -1569,6 +1578,8 @@ "https://peertube.fr", "https://peertube.amicale.net", "https://aperi.tube", - "https://tube.ac-lyon.fr" + "https://tube.ac-lyon.fr", + "https://video.lw1.at", + "https://www.yiny.org" ] } \ No newline at end of file diff --git a/src/instances/get_instances.py b/src/instances/get_instances.py index 18870f32..1fe91e17 100644 --- a/src/instances/get_instances.py +++ b/src/instances/get_instances.py @@ -260,6 +260,29 @@ def quetre(): print(Fore.GREEN + 'Fetched ' + Style.RESET_ALL + 'Quetre') +def libremdb(): + r = requests.get( + 'https://raw.githubusercontent.com/zyachel/libremdb/main/README.md') + _list = {} + _list['normal'] = [] + _list['tor'] = [] + + tmp = re.findall( + r"\| ([-a-zA-Z0-9@:%_\+.~#?&//=]{2,}\.[a-z]{2,}\b(?:\/[-a-zA-Z0-9@:%_\+.~#?&//=]*)?)*\|*[A-Z]{0,}.*\|.*\|", r.text) + + tmp = filterLastSlash(tmp) + + for item in tmp: + if item.strip() == "": + continue + if item.endswith('.onion'): + _list['tor'].append(item) + else: + _list['normal'].append(item) + mightyList['libremdb'] = _list + print(Fore.GREEN + 'Fetched ' + Style.RESET_ALL + 'Libremdb') + + def simplytranslate(): r = requests.get('https://simple-web.org/instances/simplytranslate') simplyTranslateList = {} @@ -392,6 +415,7 @@ teddit() wikiless() scribe() quetre() +libremdb() simplytranslate() linvgatranslate() searx_searxng() diff --git a/src/pages/background/background.js b/src/pages/background/background.js index 049c8258..e3e34f85 100644 --- a/src/pages/background/background.js +++ b/src/pages/background/background.js @@ -14,6 +14,7 @@ import mapsHelper from "../../assets/javascripts/maps.js"; import wikipediaHelper from "../../assets/javascripts/wikipedia.js"; import mediumHelper from "../../assets/javascripts/medium.js"; import quoraHelper from "../../assets/javascripts/quora.js"; +import libremdbHelper from "../../assets/javascripts/imdb.js"; import reutersHelper from "../../assets/javascripts/reuters.js"; import imgurHelper from "../../assets/javascripts/imgur.js"; import tiktokHelper from "../../assets/javascripts/tiktok.js"; @@ -45,6 +46,7 @@ browser.runtime.onInstalled.addListener( translateHelper.initDefaults(); mediumHelper.initDefaults(); quoraHelper.initDefaults(); + libremdbHelper.initDefaults(); reutersHelper.initDefaults(); redditHelper.initDefaults(); wikipediaHelper.initDefaults(); @@ -104,6 +106,7 @@ browser.webRequest.onBeforeRequest.addListener( if (!newUrl) newUrl = redditHelper.redirect(url, details.type, initiator); if (!newUrl) newUrl = mediumHelper.redirect(url, details.type, initiator); if (!newUrl) newUrl = quoraHelper.redirect(url, details.type, initiator); + if (!newUrl) newUrl = libremdbHelper.redirect(url, details.type, initiator); if (!newUrl) newUrl = reutersHelper.redirect(url, details.type, initiator); if (!newUrl) newUrl = imgurHelper.redirect(url, details.type, initiator); if (!newUrl) newUrl = tiktokHelper.redirect(url, details.type, initiator); @@ -182,6 +185,7 @@ async function redirectOfflineInstance(url, tabId) { if (!newUrl) newUrl = await translateHelper.switchInstance(url); if (!newUrl) newUrl = await mediumHelper.switchInstance(url); if (!newUrl) newUrl = await quoraHelper.switchInstance(url); + if (!newUrl) newUrl = await libremdbHelper.switchInstance(url); if (!newUrl) newUrl = await tiktokHelper.switchInstance(url); if (!newUrl) newUrl = await imgurHelper.switchInstance(url); if (!newUrl) newUrl = await wikipediaHelper.switchInstance(url); diff --git a/src/pages/options/index.html b/src/pages/options/index.html index 81e15321..0bb4ecde 100644 --- a/src/pages/options/index.html +++ b/src/pages/options/index.html @@ -31,6 +31,7 @@ Medium
Quora
+
IMDb
Reuters
PeerTube
LBRY/Odysee
@@ -217,6 +218,12 @@ +
+
+ IMDb +
+ +
Reuters @@ -1183,6 +1190,78 @@
+
+
+

IMDb

+
+
+
+

Enable

+ +
+
+

Protocol

+ +
+
+
+
+
+

Default Instances

+
+
+
+
+

Custom Instances

+
+
+
+ + +
+
+
+
+ + +
+
+
+
+

Default Instances

+
+
+
+
+

Custom Instances

+
+
+
+ + +
+
+
+
+
+ +

Reuters

diff --git a/src/pages/options/index.pug b/src/pages/options/index.pug index 2cd24350..9198c0f3 100644 --- a/src/pages/options/index.pug +++ b/src/pages/options/index.pug @@ -23,6 +23,7 @@ html#elementToShowWithJavaScript(lang="en") include ./widgets/wikipedia.pug include ./widgets/medium.pug include ./widgets/quora.pug + include ./widgets/imdb.pug include ./widgets/reuters.pug include ./widgets/peertube.pug include ./widgets/lbry.pug diff --git a/src/pages/options/widgets/general.js b/src/pages/options/widgets/general.js index 97854fc4..a4c5c9d2 100644 --- a/src/pages/options/widgets/general.js +++ b/src/pages/options/widgets/general.js @@ -15,6 +15,7 @@ import mapsHelper from "../../../assets/javascripts/maps.js"; import wikipediaHelper from "../../../assets/javascripts/wikipedia.js"; import mediumHelper from "../../../assets/javascripts/medium.js"; import quoraHelper from "../../../assets/javascripts/quora.js"; +import libremdbHelper from "../../../assets/javascripts/imdb.js"; import imgurHelper from "../../../assets/javascripts/imgur.js"; import tiktokHelper from "../../../assets/javascripts/tiktok.js"; import sendTargetsHelper from "../../../assets/javascripts/sendTargets.js"; @@ -132,6 +133,7 @@ resetSettings.addEventListener("click", await translateHelper.initDefaults(); await mediumHelper.initDefaults(); await quoraHelper.initDefaults(); + await libremdbHelper.initDefaults(); await reutersHelper.initDefaults(); await redditHelper.initDefaults(); await wikipediaHelper.initDefaults(); diff --git a/src/pages/options/widgets/general.pug b/src/pages/options/widgets/general.pug index b7e54902..c8e043b9 100644 --- a/src/pages/options/widgets/general.pug +++ b/src/pages/options/widgets/general.pug @@ -156,6 +156,12 @@ section#general_page.option-block x(data-localise="__MSG_quora__") Quora input#quora(type="checkbox") + div + div + img(src="../../../assets/images/imdb.svg") + x(data-localise="__MSG_imdb__") IMDb + input#imdb(type="checkbox") + div div img(src="../../../assets/images/reuters.svg") diff --git a/src/pages/options/widgets/imdb.js b/src/pages/options/widgets/imdb.js new file mode 100644 index 00000000..f2c6cba5 --- /dev/null +++ b/src/pages/options/widgets/imdb.js @@ -0,0 +1,42 @@ +import utils from "../../../assets/javascripts/utils.js"; + +const enable = document.getElementById("imdb-enable"); +const protocol = document.getElementById("imdb-protocol") +const imdb = document.getElementById('imdb_page'); + +function changeProtocolSettings() { + const normalDiv = imdb.getElementsByClassName("normal")[0]; + const torDiv = imdb.getElementsByClassName("tor")[0]; + if (protocol.value == 'normal') { + normalDiv.style.display = 'block'; + torDiv.style.display = 'none'; + } + else if (protocol.value == 'tor') { + normalDiv.style.display = 'none'; + torDiv.style.display = 'block'; + } +} + +imdb.addEventListener("change", () => { + changeProtocolSettings(); + browser.storage.local.set({ + disableImdb: !enable.checked, + imdbProtocol: protocol.value, + }) +}) + +browser.storage.local.get( + [ + "disableImdb", + "imdbProtocol" + ], + r => { + enable.checked = !r.disableImdb; + protocol.value = r.imdbProtocol; + changeProtocolSettings(); + } +) + +utils.processDefaultCustomInstances('imdb', 'libremdb', 'normal', document); +utils.processDefaultCustomInstances('imdb', 'libremdb', 'tor', document); +utils.latency('imdb', 'libremdb', document, location) \ No newline at end of file diff --git a/src/pages/options/widgets/imdb.pug b/src/pages/options/widgets/imdb.pug new file mode 100644 index 00000000..07749144 --- /dev/null +++ b/src/pages/options/widgets/imdb.pug @@ -0,0 +1,26 @@ +section#imdb_page.option-block + .some-block.option-block + h1(data-localise="__MSG_imdb__") IMDb + hr + .some-block.option-block + h4(data-localise="__MSG_enable__") Enable + input#imdb-enable(type="checkbox") + + .some-block.option-block + h4(data-localise="__MSG_protocol__") Protocol + select#imdb-protocol + option(value="normal" data-localise="__MSG_normal__") Normal + option(value="tor" data-localise="__MSG_tor__") Tor + + #libremdb + hr + .normal + include ../../widgets/instances.pug + +instances('https://libremdb.com') + include ../../widgets/latency.pug + +latency('libremdb') + .tor + include ../../widgets/instances.pug + +instances('https://libremdb.onion') + + script(type="module" src="./widgets/imdb.js") \ No newline at end of file diff --git a/src/pages/popup/popup.html b/src/pages/popup/popup.html index 8b5b80a5..91fd113b 100644 --- a/src/pages/popup/popup.html +++ b/src/pages/popup/popup.html @@ -52,6 +52,10 @@

Quora

+
+

IMDb

+ +

Reuters

diff --git a/src/pages/popup/popup.pug b/src/pages/popup/popup.pug index e9af3900..47113a13 100644 --- a/src/pages/popup/popup.pug +++ b/src/pages/popup/popup.pug @@ -67,6 +67,12 @@ html(lang="en") img(src="../../assets/images/quora.png") h4(data-localise="__MSG_quora__") Quora input#disable-quora(type="checkbox") + + #imdb.some-block + a.title(href="https://imdb.com") + img(src="../../assets/images/imdb.svg") + h4(data-localise="__MSG_imdb__") IMDb + input#disable-imdb(type="checkbox") #reuters.some-block a.title(href="https://reuters.com") diff --git a/src/pages/widgets/links.pug b/src/pages/widgets/links.pug index 436b3b22..b2dca780 100644 --- a/src/pages/widgets/links.pug +++ b/src/pages/widgets/links.pug @@ -46,6 +46,10 @@ mixin links(service) img(src="../../../assets/images/quora.png") a(href="#quora" data-localise="__MSG_quora__") Quora + .title + img(src="../../../assets/images/imdb.svg") + a(href="#imdb" data-localise="__MSG_imdb__") IMDb + .title img(src="../../../assets/images/reuters.svg") a(href="#reuters" data-localise="__MSG_reuters__") Reuters -- cgit 1.4.1