Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions ophirofox/content_scripts/liberation.css
Original file line number Diff line number Diff line change
@@ -1,16 +1,31 @@
.ophirofox-europresse {
font-size: 0.875rem;
padding: 5px 10px;
background-color: rgb(250, 235, 29);
font-family: TiemposText;
color: #000;
border-radius: 4px;
text-decoration: none;
display: block;
margin-top: -2rem; /* negative margin to pull it up next to h1 */
margin-left: auto; /* pushes to right if there's space */
width: fit-content;
display: flex;
float: left;
padding: 0px 5px 0px 5px;
margin-right: 0.75rem;
row-gap: 4px;
column-gap: 4px;
height: 1.5rem;
padding-top: 0px;
padding-right: 4px;
-webkit-box-align: center;
align-items: center;
background-color: rgb(250, 235, 29);
width: max-content;
border-top-left-radius: 4px;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom-left-radius: 4px;
}

.ophirofox-europresse:hover {
color: #666666;
text-decoration: underline;
color: #000;
}
125 changes: 52 additions & 73 deletions ophirofox/content_scripts/liberation.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,100 +4,79 @@ function extractKeywords() {
.getAttribute("content");
}

/**
* Crée un lien vers Europresse avec les keywords donnés
* @param {string} publishedTime - article publication date (2024-01-01)
* @returns {Promise<HTMLAnchorElement>}
*/
async function createLink(publishedTime) {
const a = await ophirofoxEuropresseLink(extractKeywords(), { publishedTime: publishedTime });
return a;
}

function findPremiumBanner() {
const anchor = document.querySelector('div[class^=article-body-paywall]');
if (!anchor) {
return;
}
return anchor;
return document.querySelector('div[class^=article-body-paywall]') || null;
}

function findInsertionPoint() {
const target = 'Réservé aux abonnés'.normalize('NFC');
const el = [...document.querySelectorAll('span')].find(
s => s.textContent.normalize('NFC').trim() === target
);
return el?.parentElement ?? null;
}

async function injectLink(publishedDate) {
if (document.querySelector('div[class^=article-body-paywall] + a.ophirofox-europresse')) return;
const anchor = findInsertionPoint();
if (!anchor) return;
const link = await createLink(publishedDate);
anchor.after(link);
console.log('Ophirofox injected');
}

async function onLoad(premiumBanner) {
/*
The UI is reactive (and DOM rewritten), so we need to wait for some nodes to be rewritten to the DOM
before we can add our link. It seems that the React components are added to the DOM in a particular order.
function resolvePublishedDate() {
let publishedDate = document.querySelector(
"meta[property='article:published_time'], meta[property='og:article:published_time'], meta[property='date:published_time']"
)?.getAttribute("content") || '';

const fusionMetadata = document.getElementById('fusion-metadata');
if (fusionMetadata?.textContent) {
const match = /"first_publish_date":"(\d{4}-\d{2}-\d{2}[A-Z]+\d{2}:\d{2}:\d{2}\.[0-9+-:]+Z)"/.exec(fusionMetadata.textContent);
if (match) {
const firstPublishedDateInstance = new Date(match[1]);
if (!isNaN(firstPublishedDateInstance)) {
if (!publishedDate.trim() || firstPublishedDateInstance < new Date(publishedDate)) {
publishedDate = match[1];
}
}
} else {
console.error("No match for 'first_publish_date' found.");
}
} else {
console.error("'fusion-metadata' element not found or empty.");
}

With heavy loading, the MutationObserver execution is too late, and only catch .dossier-feed class.
After caching, we can rely on the .article-body-paywall added node.
return publishedDate;
}

Weird choices for a nearly-static content-driven website with SEO concerns.
*/
async function onLoad() {
const observer = new MutationObserver(async mutationsList => {
for (let mutation of mutationsList) {
if (mutation.addedNodes.length > 0) {
const addedNode = mutation.addedNodes[0];
if (addedNode.classList.contains('dossier-feed') ||
if (
addedNode.classList.contains('dossier-feed') ||
addedNode.classList.contains('article-body-paywall')
) {
observer.disconnect();

// Not sure if premiumBanner is (and will be) still valid after DOM rewrite
if (!document.querySelector('div[class^=article-body-paywall] + a.ophirofox-europresse')) {
// See #239, Libération replaces date:published_time with the date of edit, which means that a search limited by the time of publication may be too restrictive
// We need to specify the date to use for the generic ophirofoxEuropresseLink function
// Might need refactor if other medias have the same problem, more properties for fail-safe
let publishedDate = document.querySelector("meta[property='article:published_time'], meta[property='og:article:published_time'], meta[property='date:published_time']")?.getAttribute("content") || '';

let firstPublishedDate = null;
let firstPublishedDateInstance = null;

let fusionMetadata = document.getElementById('fusion-metadata');
if (fusionMetadata && fusionMetadata.textContent) {
let match = /"first_publish_date":"(\d{4}-\d{2}-\d{2}[A-Z]+\d{2}:\d{2}:\d{2}\.[0-9+-:]+Z)"/.exec(fusionMetadata.textContent); // 2024-08-27T18:18:55.663Z => UTC
if (match) {
firstPublishedDate = match[1];
firstPublishedDateInstance = new Date(firstPublishedDate);
} else {
console.error("No match for 'first_publish_date' found.");
}
} else {
console.error("'fusion-metadata' element not found or empty.");
}

// Check if publishedDate exists and is not empty
if (publishedDate && publishedDate.trim()) {
// If the first published date is valid and older
if (firstPublishedDateInstance && !isNaN(firstPublishedDateInstance) && (firstPublishedDateInstance < new Date(publishedDate))) {
publishedDate = firstPublishedDate;
}
} else {
// If published date is empty or invalid, use firstPublishedDate if available
if (firstPublishedDateInstance && !isNaN(firstPublishedDateInstance)) {
publishedDate = firstPublishedDate;
}
}

const link = await createLink(publishedDate);
document.querySelector('h1').after(link);

console.log('Ophirofox injected after React DOM rewrite');
break;
}
await injectLink(resolvePublishedDate());
break;
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
observer.observe(document.body, { childList: true, subtree: true });
}

createLink().then(link => {
const premiumBanner = findPremiumBanner();
if (premiumBanner) {
premiumBanner.after(link);
console.log('Ophirofox injected');
(async () => {
if (findPremiumBanner()) {
await injectLink(resolvePublishedDate());
}
onLoad(premiumBanner);
});
onLoad();
})();
Loading