|
// ==UserScript== |
|
// @name The Old New Thing - Tweaks |
|
// @version 1 |
|
// @grant none |
|
// @run-at document-start |
|
// @include https://devblogs.microsoft.com/oldnewthing/* |
|
// ==/UserScript== |
|
|
|
// URL_MATCHERS[hostname] == [ pathname_regex, pathname_regex, ... ] |
|
const OLD_URL_MATCHERS = new Map([ |
|
["weblogs.asp.net", [ |
|
/^[\/\\]+oldnewthing[\/\\]+archive[\/\\]+(?<year>\d{4})[\/\\]+(?<month>\d\d)[\/\\]+(?<day>\d\d)[\/\\]+\d+\.aspx$/, |
|
]], |
|
["devblogs.microsoft.com", [ |
|
/^[\/\\]+oldnewthing[\/\\]+archive[\/\\]+(?<year>\d{4})[\/\\]+(?<month>\d\d)[\/\\]+(?<day>\d\d)[\/\\]+\d+\.aspx$/i, |
|
]] |
|
]); |
|
|
|
function url_for_date(month, day, year) { |
|
day = day.toString().padStart(2, "0"); |
|
month = month.toString().padStart(2, "0"); |
|
return `https://devblogs.microsoft.com/oldnewthing/${year}${month}${day}-00/`; |
|
} |
|
function url_for_month(month, year) { |
|
month = month.toString().padStart(2, "0"); |
|
return `https://devblogs.microsoft.com/oldnewthing/${year}/${month}/`; |
|
} |
|
|
|
function upgrade_url(url) { |
|
// Observed formats: |
|
// http://weblogs.asp.net/oldnewthing/archive/2004/01/30/65013.aspx |
|
// https://devblogs.microsoft.com/oldnewthing/archive/2004/02/03/66660.aspx |
|
// https://devblogs.microsoft.com/oldnewthing/20040203-00/?p=40763 |
|
|
|
if (!(url instanceof URL)) |
|
url = new URL(url); |
|
|
|
let hostname = url.hostname.toLowerCase(); |
|
let pathname = url.pathname; |
|
for(const [key, list] of OLD_URL_MATCHERS.entries()) { |
|
if (hostname != key) |
|
continue; |
|
for(let regex of list) { |
|
let match = pathname.match(regex); |
|
if (match) |
|
return url_for_date(+match.groups.month, match.groups.day, match.groups.year); |
|
} |
|
} |
|
|
|
return null; |
|
} |
|
|
|
function upgrade_link(node) { |
|
let upgraded = upgrade_url(node.href); |
|
if (upgraded !== null) { |
|
node.setAttribute("data-orig-url", node.href); |
|
node.href = upgraded; |
|
} |
|
} |
|
|
|
function enhance_pagination() { |
|
let container = document.getElementById("primary"); |
|
if (!container) |
|
return; |
|
let pagination = container.querySelector(":scope>nav.pagination-container"); |
|
if (!pagination) |
|
return; |
|
|
|
// |
|
// Add pagination between months. |
|
// |
|
let this_date_match = window.location.pathname.match(/[\\\/]+(?<year>\d{4})[\\\/]+(?<month>\d\d)(?:[\\\/]+page[\\\/]+\d+[\\\/]*)?$/); |
|
if (this_date_match) { |
|
let month = +this_date_match.groups.month; |
|
let year = +this_date_match.groups.year; |
|
|
|
let ul = pagination.querySelector("ul"); |
|
let first = ul.firstElementChild; |
|
let last = ul.lastElementChild; |
|
|
|
let add_future_month = false; |
|
let add_past_month = false; |
|
|
|
if (first.classList.contains("active")) { |
|
add_future_month = true; |
|
if (first == last) { |
|
add_past_month = true; |
|
} |
|
} else if (last.classList.contains("active")) { |
|
add_past_month = true; |
|
} |
|
|
|
if (add_future_month || add_past_month) { |
|
let archives = document.querySelector("ul.ftr-archives"); |
|
if (archives) { |
|
let format = new Intl.DateTimeFormat(undefined, { year: "numeric", month: "long" }); |
|
function _format(month, year) { |
|
let date = new Date(year, month - 1); |
|
return format.format(date); |
|
} |
|
|
|
if (add_future_month) { |
|
let dm = month; |
|
let dy = year; |
|
if (dm == 12) { |
|
dm = 1; |
|
++dy; |
|
} else { |
|
++dm; |
|
} |
|
|
|
let newest = archives.firstElementChild.querySelector("a").href.match(/[\\\/]+(?<year>\d{4})[\\\/]+(?<month>\d\d)[\\\/]*$/); |
|
if (newest) { |
|
let ny = +newest.groups.year; |
|
let nm = +newest.groups.month; |
|
if (dy < ny || (dy == ny && dm <= nm)) { |
|
let li = document.createElement("li"); |
|
let a = document.createElement("a"); |
|
li.className = "page-item"; |
|
a.href = url_for_month(dm, dy); |
|
a.textContent = _format(dm, dy); |
|
a.className = "page-link"; |
|
|
|
/* |
|
let chevron = document.createElement("i"); |
|
chevron.className = "fabric-icon fabric-icon--ChevronLeft"; |
|
a.insertBefore(chevron, a.firstChild); |
|
//*/ |
|
|
|
li.append(a); |
|
ul.insertBefore(li, ul.firstElementChild); |
|
} |
|
} |
|
} |
|
if (add_past_month) { |
|
let dm = month; |
|
let dy = year; |
|
if (dm == 1) { |
|
dm = 12; |
|
--dy; |
|
} else { |
|
--dm; |
|
} |
|
|
|
let oldest = archives.lastElementChild.querySelector("a").href.match(/[\\\/]+(?<year>\d{4})[\\\/]+(?<month>\d\d)[\\\/]*$/); |
|
if (oldest) { |
|
let oy = +oldest.groups.year; |
|
let om = +oldest.groups.month; |
|
if (dy > oy || (dy == oy && dm >= om)) { |
|
let li = document.createElement("li"); |
|
let a = document.createElement("a"); |
|
li.className = "page-item"; |
|
a.href = url_for_month(dm, dy); |
|
a.textContent = _format(dm, dy); |
|
a.className = "page-link"; |
|
|
|
/* |
|
let chevron = document.createElement("i"); |
|
chevron.className = "fabric-icon fabric-icon--ChevronRight"; |
|
a.append(chevron); |
|
//*/ |
|
|
|
li.append(a); |
|
ul.append(li); |
|
} |
|
} |
|
|
|
} |
|
} |
|
} |
|
} |
|
|
|
// Add top-side pagination. |
|
container.insertBefore(pagination.cloneNode(true), container.firstElementChild); |
|
} |
|
|
|
if (document.readyState == "loading") { |
|
let OBSERVER = new MutationObserver(function(records, observer) { |
|
for(const record of records) { |
|
for (const node of record.addedNodes) { |
|
if ((node.nodeName == "A" || node.nodeName == "a") && !node.classList.contains("page-link")) { |
|
upgrade_link(node); |
|
continue; |
|
} |
|
} |
|
} |
|
}); |
|
OBSERVER.observe(document.documentElement, { |
|
subtree: true, |
|
childList: true, |
|
}); |
|
document.addEventListener("DOMContentLoaded", function() { |
|
OBSERVER.disconnect(); |
|
enhance_pagination(); |
|
}); |
|
} else { |
|
document.querySelectorAll("a[href]").forEach(function(node) { |
|
upgrade_link(node); |
|
}); |
|
enhance_pagination(); |
|
} |