NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name PostAge // @namespace DScript // @version 0.2.9 // @description Unify online publication dates; draw attention to old articles // @author DScript // @match https://www.svt.se/* // @match https://www.dn.se/* // @match https://www.aftonbladet.se/* // @match https://www.expressen.se/* // @match https://www.svd.se/* // @match https://www.metro.se/* // @match https://www.di.se/* // @match https://nyheter24.se/* // @match https://newsvoice.se/* // @match https://www.theguardian.com/* // @match http://www.dailymail.co.uk/* // @match https://www.huffingtonpost.com/* // @match https://*.wordpress.com/* // @match http://scienceblogs.com/* // @match http://www.gp.se/* // @match http://www.hn.se/* // @match http://www.ttela.se/* // @match http://www.bohuslaningen.se/* // @match http://www.stromstadstidning.se/* // @match http://www.hallandsposten.se/* // @match https://www.sydsvenskan.se/* // @match https://www.hd.se/* // @match https://www.etc.se/* // @match http://www.bbc.com/* // @match http://www.bbc.co.uk/* // @match https://www.washingtonpost.com/* // @match https://www.nytimes.com/* // @match http://*.cnn.com/* // @match http://www.foxnews.com/* // @match https://www.nbcnews.com/* // @match https://www.wsj.com/* // @match http://www.latimes.com/* // @match https://www.today.com/* // @match http://abcnews.go.com/* // @match http://www.dt.se/* // @grant none // @require http://code.jquery.com/jquery-2.2.3.min.js // @updateURL https://openuserjs.org/meta/DScript/PostAge.meta.js // ==/UserScript== /* TODO: - Stampen: scrolling into new articles - Yahoo news doesn't update on navigation - Sydsvenskan: not ideal when scrolling into new articles. also some cases of navigation not detected - SR: currently commented out. needs to update when navigating // @match http://sverigesradio.se/* */ (function() { 'use strict'; function debounce(fn, delay) { var timer = null; return function () { var context = this, args = arguments; clearTimeout(timer); timer = setTimeout(function () { fn.apply(context, args); }, delay); }; } // shorthand for handlesUrl that simply checks if the url contains a substring function urlContains() { var validUrls = Array.prototype.slice.call(arguments, 0); return (url) => validUrls.some(u => url.toLowerCase().indexOf(u) >= 0); } var postAge = { debug: false, colors: { recent: { color: '#3c763d', 'background-color': '#dff0d8', 'border-color': '#d6e9c6' }, moderate: { color: '#8a6d3b', 'background-color': '#fcf8e3', 'border-color': '#faebcc' }, old: { color: '#a94442', 'background-color': '#f2dede', 'border-color': '#ebccd1' } }, parsers: [], log: function(l) { if(!this.debug) return; console.log.apply(null, arguments); }, addParser: function(name,parser) { if(!parser) return; parser.name = name; this.parsers.push(parser); }, findParser: function(url) { return this.parsers.find(function(p) { try { postAge.log(p); var h = p.handlesUrl(url); postAge.log('Handled by ' + p.name + '? ' + (h ? 'YES' : 'NO')); return h; } catch(ex) { postAge.log('ERROR testing ' + p.name, + ex.message); return false; } }); }, formatDate: function(dt) { return [dt.getFullYear(), ('0' + (dt.getMonth()+1)).slice(-2), ('0' + dt.getDate()).slice(-2)].join('-'); }, showDates: function(dates) { if(typeof dates !== 'object') { $('.postAgePopover').hide(); return; }; dates = dates.filter(d => d.date instanceof Date && isFinite(d.date)); if (dates.length == 0) { $('.postAgePopover').hide(); return; }; var popover = $('.postAgePopover'); if(popover.length === 0) popover = $('<div/>', { 'class': 'postAgePopover' }) .css({ position: 'fixed', top: 50, left: 10, padding: 15, 'border-radius': 15, 'border-style': 'solid', 'border-width': 1, 'z-index': 1000000071 }) .appendTo(document.body); popover.empty(); var minDate = new Date(); dates.forEach(function(dt) { var item = $('<div/>').appendTo(popover); $('<span/>', { 'class': 'title', text: dt.title }) .css({ 'font-family': 'verdana', 'font-size': 11, 'font-weight': 'bold', display: 'inline-block', width: 100 }) .appendTo(item); $('<span/>', { 'class': 'date', text: postAge.formatDate(dt.date) }) .css({ 'font-family': 'verdana', 'font-size': 11 }) .appendTo(item); if(dt.date < minDate) minDate = dt.date; }); var age = (new Date() - minDate) / (1000*60*60); var color = this.colors.old; if(age < (24*182)) color = this.colors.moderate; if(age < (24*7)) color = this.colors.recent; popover.css(color); postAge.log('Age: ' + age + ' (' + (typeof age) + ')'); if(age > 24*365) { age = (age/24/365).toFixed(1) + 'y'; } else if(age > 24*30) { age = Math.floor(age/24/30) + 'm'; postAge.log(age); } else if(age > 24) { age = Math.floor(age/24) + 'd'; postAge.log(age); } else { age = Math.floor(age) + 'h'; postAge.log(age); } var h2 = $('<h2/>', { text: age }) .css({ 'font-family': 'georgia', 'font-size': 30, 'font-weight': 'bold', 'text-align': 'center', margin: 5, color: color.color }) .insertBefore(popover.find('div:first')); $('<button/>', { html: '×', 'type': 'button' }) .click(function() { popover.hide(); }).css(color) .css({ 'float': 'right', 'border-style': 'solid', 'border-width': 1, 'border-radius': '50%', 'border-color': color.color }) .insertBefore(h2); }, exec: function() { var parser = this.findParser(location.href); if(typeof parser !== 'object') return; try { var out = parser.run(); this.log('Data from ' + parser.name + ':'); this.log(out); this.showDates(out); } catch(ex) { postAge.log('ERROR executing ' + parser.name, ex); } }, monitoring: false, monitorDom: function($node, key) { if($node.length==0) return; postAge.log('monitor: ' + $node.length); var mo = window.MutationObserver || window.WebKitMutationObserver; var ctx = this; var observer = new mo(debounce(function(mutations, observer) { ctx.log('domchange', key||''); ctx.exec(); }, 500)); observer.observe($node[0], { childList: true, subtree: true }); postAge.monitoring = true; postAge.log(observer); } }; postAge.addParser('SVT Nyheter', { handlesUrl: urlContains('www.svt.se'), run: function() { if(!postAge.monitoring) { postAge.monitorDom($('.nyh_body')); } var fixTime = (time) => { var dt = time.attr('datetime').replace(/(\d+)\.(\d+)$/, '$1:$2'); var months = {januari:'jan',februari:'feb',mars:'mar',april:'apr',maj:'may',juni:'jun',juli:'jul',augusti:'aug',oktober:'oct',november:'nov',december:'dec'}; Object.keys(months).forEach(k => dt = dt.replace(k,months[k])); return new Date(dt); }; return $('.nyh_article__date').map(function() { return { title: $('span',this).text().replace(':',''), date: fixTime($('time',this)) }; }).toArray(); } }); postAge.addParser('DN', { handlesUrl: urlContains('www.dn.se'), run: function() { var parser = this; return $('.article__byline:first meta').map(function() { return { title: parser.resolveTitle($(this).attr('itemprop')), date: new Date($(this).attr('content')) }; }).toArray(); }, resolveTitle: function(itemProp) { switch(itemProp) { case 'dateModified': return 'Edited'; case 'datePublished': return 'Published'; default: return ''; } } }); postAge.addParser('Aftonbladet', { handlesUrl: urlContains('www.aftonbladet.se'), run: function() { var dates = this.getFromAbSe(); if(dates.length == 0) dates = this.getFromTimestamp(); return dates; }, getFromAbSe: function() { var dates = []; if(typeof ABse !== 'object' || !('page' in ABse)) return dates; if('articlePublishedDateTime' in ABse.page) dates.push({ title: 'Published', date: new Date(ABse.page.articlePublishedDateTime) }); if('articleLastModifiedDateTime' in ABse.page) dates.push({ title: 'Edited', date: new Date(ABse.page.articleLastModifiedDateTime) }); return dates; }, getFromTimestamp: function() { return [{ title: 'Published', date: new Date($('.abArticle .abTimestamp:first time').attr('datetime')) }]; } }); postAge.addParser('Expressen', { handlesUrl: function(url) { return typeof Ariel === 'object' && 'pagedata' in Ariel && 'trackingInfo' in Ariel.pagedata && 'burt' in Ariel.pagedata.trackingInfo && 'desktop' in Ariel.pagedata.trackingInfo.burt && 'annotations' in Ariel.pagedata.trackingInfo.burt.desktop; }, run: function() { var ann = Ariel.pagedata.trackingInfo.burt.desktop.annotations; var dt = ann.filter(function(a) { return a[1] == 'published_date'; })[0]; if(!!dt) return [{ title: 'Published', date: new Date(dt[2]) }]; return []; } }); postAge.addParser('SvD', { handlesUrl: urlContains('www.svd.se'), run: () => [{ title: 'Published', date: new Date($('meta[name="publishdate"]').attr('content')) }] }); postAge.addParser('OpenGraph websites', { handlesUrl: (url) => $('meta[property="article:published_time"]').length > 0, run: () => [ { title: 'Published', date: new Date($('meta[property="article:published_time"]').attr('content')) }, { title: 'Edited', date: new Date($('meta[property="article:modified_time"]').attr('content')) } ] }); postAge.addParser('Stampen', { handlesUrl: urlContains('www.gp.se','www.hn.se','www.ttela.se','www.stromstadstidning.se','www.hallandsposten.se','www.bohuslaningen.se'), run: function() { if(!postAge.monitoring) { postAge.monitorDom($('.nyh_body')); } var fixTime = (time) => { var dt = time.attr('datetime'); var tmatch = $('.article__meta__published time').text().match(/\d+:\d+/); return new Date([dt, tmatch.length > 0 ? tmatch[0] : '00:00'].join(' ')); }; return [ { title: 'Published', date: fixTime($('.article__meta__published time')) } ]; } }); postAge.addParser('Sydsvenskan / HD', { handlesUrl: urlContains('sydsvenskan.se','www.hd.se'), run: () => 'hdsconfig' in window && 'published' in window.hdsconfig ? [{ title: 'Published', date: new Date(window.hdsconfig.published) }] : null }); postAge.addParser('Dagens ETC', { handlesUrl: urlContains('www.etc.se'), run: () => [{ title: 'Published', date: new Date($('meta[name="dcterms.date"]').attr('content'))}] }); postAge.addParser('BBC', { handlesUrl: (url) => urlContains('www.bbc.co')(url) && $('script[type="application/ld+json"]').length > 0, run: function() { var data = JSON.parse($('script[type="application/ld+json"]').text().trim()); return [{ title: 'Published', date: new Date(data.datePublished) }]; } }); postAge.addParser('Washington Post', { handlesUrl: urlContains('www.washingtonpost.com'), run: function() { var date = new Date($('.pb-timestamp[itemprop="datePublished"]').attr('content').replace(/\-500$/,'-0500')); return [{ title: 'Published', date: date }]; } }); postAge.addParser('NY Times', { handlesUrl: urlContains('www.nytimes.com'), run: () => [ { title: 'Published', date: new Date($('meta[property="article:published"]').attr('content')) }, { title: 'Edited', date: new Date($('meta[property="article:modified"]').attr('content')) } ] }); postAge.addParser('Yahoo News', { handlesUrl: urlContains('.yahoo.com'), run: () => [{ title: 'Published', date: new Date($('.auth-attr time').attr('datetime')) }] }); postAge.addParser('CNN', { handlesUrl: urlContains('.cnn.com'), run: () => [ { title: 'Published', date: new Date($('meta[property="og:pubdate"]').attr('content')) }, { title: 'Edited', date: new Date($('meta[name="lastmod"]').attr('content')) } ] }); postAge.addParser('Fox News', { handlesUrl: urlContains('www.foxnews.com'), run: () => [ { title: 'Published', date: new Date($('meta[name="dcterms.created"]').attr('content')) }, { title: 'Edited', date: new Date($('meta[name="dcterms.modified"]').attr('content')) } ] }); postAge.addParser('NBC News', { handlesUrl: urlContains('www.nbcnews.com'), run: () => [ { title: 'Published', date: new Date($('.timestamp_article[itemprop="dateModified"]').attr('datetime')) }] }); postAge.addParser('Wall Street Journal', { handlesUrl: urlContains('.wsj.com'), run: () => [ { title: 'Published', date: new Date($('meta[name="article.published"]').attr('content')) }, { title: 'Edited', date: new Date($('meta[name="article.updated"]').attr('content')) } ] }); postAge.addParser('LA Times', { handlesUrl: urlContains('www.latimes.com'), run: () => [ { title: 'Published', date: new Date($('meta[itemprop="datePublished"]').attr('content')) }, { title: 'Edited', date: new Date($('meta[itemprop="dateModified"]').attr('content')) } ] }); postAge.addParser('Today.com', { handlesUrl: urlContains('www.today.com'), run: () => [{ title: 'Published', date: new Date(analyticsDataLayer.publishDate) }] }); postAge.addParser('Dalarnas Tidningar', { handlesUrl: () => 'PageObject' in window && 'metadata' in window.PageObject && 'pub_date' in window.PageObject.metadata, run: () => [{ title: 'Published', date: new Date(window.PageObject.metadata.pub_date) }] }); postAge.addParser('ABC News', { handlesUrl: () => urlContains('abcnews.go.com'), run: () => [ { title: 'Published', date: new Date($('meta[name="DC.date.issued"]').attr('content')) }, { title: 'Edited', date: new Date($('meta[name="Last-Modified"]').attr('content')) } ] }); /* postAge.addParser('Sveriges Radio', { handlesUrl: (url) => url.toLowerCase().indexOf('sverigesradio.se') >= 0, run: function() { if(!postAge.monitoring) { postAge.monitorDom($('body'), 'body'); postAge.monitorDom($('.sr-page__wrapper'), 'sr-page__wrapper'); } postAge.log('Exec'); var meta = $('meta[name="displaydate"]'); var dates = []; if(meta.length > 0) { var dstr = meta.attr('content'); dates.push({ title: 'Published', date: new Date(dstr.slice(0,4), dstr.slice(4,6), dstr.slice(6,8), 0, 0, 0, 0)}); } else { // fallback var dateMatch = $('.article-details__published').text().trim().match(/Publicerat (.+) kl (\d+)\.(\d+)/); if(dateMatch) { var months = {januari:'jan',februari:'feb',mars:'mar',april:'apr',maj:'may',juni:'jun',juli:'jul',augusti:'aug',oktober:'oct',november:'nov',december:'dec'}; Object.keys(months).forEach(k => dateMatch[1] = dateMatch[1].replace(k,months[k])); postAge.log(match); dates.push({ title: 'Published', date: new Date(match[1] + ' ' + match[2] + ':' + match[3]) }); } } return dates; } });*/ $(function(){ postAge.exec(); }); })();