The Information

Project Viewer + Profile Viewer + Alternative Articles + Enhanced Article Experience

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(Tôi đã có Trình quản lý tập lệnh người dùng, hãy cài đặt nó!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name The Information
// @namespace http://tampermonkey.net/
// @version 2.0
// @description Project Viewer + Profile Viewer + Alternative Articles + Enhanced Article Experience
// @author UniverseDev
// @icon https://www.google.com/s2/favicons?domain=theinformation.com&sz=64
// @license MIT
// @match https://www.theinformation.com/*
// @grant GM.xmlHttpRequest
// ==/UserScript==
(function() {
    'use strict';
    const modalCSS = `
        #premiumModalOverlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.65);
            z-index: 10000;
            display: flex;
            align-items: center;
            justify-content: center;
            animation: fadeIn 0.4s ease-out forwards;
        }
        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }
        #premiumModalContent {
            background: linear-gradient(135deg, #ffffff 0%, #f7f7f7 100%);
            border-radius: 12px;
            padding: 25px 35px;
            max-width: 500px;
            width: 90%;
            box-shadow: 0 12px 30px rgba(0, 0, 0, 0.25);
            font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
            position: relative;
            animation: slideIn 0.5s ease-out forwards;
        }
        @keyframes slideIn {
            from { transform: translateY(-20px); opacity: 0; }
            to { transform: translateY(0); opacity: 1; }
        }
        #premiumModalClose {
            position: absolute;
            top: 12px;
            right: 15px;
            background: none;
            border: none;
            font-size: 28px;
            line-height: 1;
            color: #aaa;
            cursor: pointer;
            transition: color 0.3s ease;
        }
        #premiumModalClose:hover {
            color: #e74c3c;
        }
        #premiumModalHeader {
            font-size: 24px;
            margin-bottom: 12px;
            font-weight: 600;
            color: #2c3e50;
            text-align: center;
            border-bottom: 1px solid #ddd;
            padding-bottom: 8px;
        }
        #premiumModalContent p {
            margin: 12px 0;
            line-height: 1.6;
            color: #34495e;
            font-size: 15px;
        }
        #premiumModalContent a {
            color: #2980b9;
            text-decoration: none;
            font-weight: 500;
        }
        #premiumModalContent a:hover {
            text-decoration: underline;
        }
    `;
    const styleElem = document.createElement('style');
    styleElem.type = 'text/css';
    styleElem.textContent = modalCSS;
    document.head.appendChild(styleElem);
    const enhancedStyle = document.createElement('style');
    enhancedStyle.textContent = `
        .article-display {
            margin: 20px 20px 40px 20px;
            padding: 20px;
            background: #ffffff;
            font-family: Arial, sans-serif;
        }
        .article-picture {
            max-width: 100%;
            height: auto;
            margin-bottom: 10px;
        }
        .authors {
            display: flex;
            flex-wrap: wrap;
            gap: 20px;
            margin-top: 10px;
        }
        .author {
            max-width: 300px;
            border: 1px solid #ccc;
            padding: 15px;
            border-radius: 5px;
        }
        .author img {
            width: 80px;
            height: 80px;
            border-radius: 50%;
            margin-bottom: 10px;
        }
        .related-article {
            display: flex;
            flex-direction: column;
            margin-bottom: 20px;
            padding: 16px;
            border-top: 1px solid #ccc;
        }
        .related-article img {
            width: 100%;
            max-width: 300px;
            height: auto;
            margin-bottom: 16px;
        }
        .related-article .text {
            flex: 1;
        }
        .related-article .highlight {
            font-weight: bold;
            color: magenta;
            margin-bottom: 12px;
        }
        .related-article h4 {
            margin: 0 0 12px 0;
            font-size: 1.2em;
        }
        .related-article .meta {
            color: gray;
            font-size: 0.9em;
        }
        .related-article .excerpt {
            font-size: 0.9em;
            color: #555;
            margin-top: 10px;
        }
        .related-article .comments {
            color: #666;
            font-size: 0.9em;
            margin-top: 5px;
        }
        .json-buttons {
            display: flex;
            gap: 10px;
            margin-top: 20px;
        }
        .json-toggle-btn {
            padding: 5px 10px;
            font-size: 0.9em;
            background-color: #ffffff;
            color: #007bff;
            border: 2px solid #007bff;
            cursor: pointer;
            border-radius: 5px;
            transition: background-color 0.3s, color 0.3s;
        }
        .json-toggle-btn:hover {
            background-color: #007bff;
            color: #ffffff;
        }
        .json-data {
            display: none;
            margin-top: 20px;
            padding: 10px;
            background: #eee;
            border: 1px solid #ccc;
            white-space: pre-wrap;
            word-wrap: break-word;
            overflow-x: auto;
        }
        .article-display a {
            color: inherit;
            text-decoration: none;
        }
        .article-display a:hover {
            color: #ff00ff;
            text-decoration: none;
        }
        .exclusive {
            font-weight: bold;
            color: #ff00ff;
        }
        .exclusive-article {
            border: 2px solid #ff00ff;
            background-color: #fff0f5;
            padding: 10px;
        }
        .article-display .summary a {
            font-weight: bold;
        }
        @media (min-width: 640px) {
            .related-article {
                flex-direction: row;
            }
            .related-article img {
                margin-right: 24px;
                margin-bottom: 0;
            }
        }
    `;
    document.head.appendChild(enhancedStyle);
    function createPremiumModal(dataset) {
        const overlay = document.createElement('div');
        overlay.id = 'premiumModalOverlay';
        const modal = document.createElement('div');
        modal.id = 'premiumModalContent';
        const closeButton = document.createElement('button');
        closeButton.id = 'premiumModalClose';
        closeButton.innerHTML = '×';
        closeButton.addEventListener('click', () => overlay.remove());
        modal.appendChild(closeButton);
        const header = document.createElement('div');
        header.id = 'premiumModalHeader';
        header.textContent = dataset.name || 'Project Information';
        modal.appendChild(header);
        if (dataset.description) {
            const description = document.createElement('p');
            description.textContent = dataset.description;
            modal.appendChild(description);
        }
        if (dataset.url) {
            const projLink = document.createElement('p');
            projLink.innerHTML = `<a href="${dataset.url}" target="_blank">Visit Project Page</a>`;
            modal.appendChild(projLink);
        }
        if (dataset.distribution && Array.isArray(dataset.distribution)) {
            const downloadHeader = document.createElement('p');
            downloadHeader.style.fontWeight = '600';
            downloadHeader.style.marginTop = '18px';
            downloadHeader.textContent = 'Download Data:';
            modal.appendChild(downloadHeader);
            dataset.distribution.forEach(dist => {
                if (dist.contentUrl && dist.encodingFormat) {
                    const dlLink = document.createElement('p');
                    dlLink.innerHTML = `<a href="${dist.contentUrl}" target="_blank">Download (${dist.encodingFormat})</a>`;
                    modal.appendChild(dlLink);
                }
            });
        }
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
    }
    function extractDataset() {
        const scripts = document.querySelectorAll('script[type="application/ld+json"]');
        let dataset = null;
        scripts.forEach(script => {
            try {
                let jsonData = script.textContent.trim();
                jsonData = jsonData.replace(/[-\u001F\u007F-\u009F]/g, "").replace(/\r?\n|\r/g, " ");
                const parsedData = JSON.parse(jsonData);
                if (Array.isArray(parsedData)) {
                    parsedData.forEach(item => {
                        if (item['@type'] === 'Dataset') {
                            dataset = item;
                        }
                    });
                } else if (parsedData['@type'] === 'Dataset') {
                    dataset = parsedData;
                }
            } catch (e) {}
        });
        return dataset;
    }
    function renderProfile(data) {
        const person = data.mainEntity || {};
        const name = person.name || 'Unknown Author';
        const image = person.image || 'https://tii.imgix.net/production/articles/article_default_rebrand.jpg?auto=format&w=250';
        const url = person.url || '';
        const description = person.description || 'No description available.';
        const articles = person.hasPart ? person.hasPart.filter(item => item['@type'] === 'Article') : [];
        let twitterLink = '';
        let linkedinLink = '';
        let instagramLink = '';
        if (person.sameAs && Array.isArray(person.sameAs)) {
            const twitterUrl = person.sameAs.find(url => url.includes('twitter.com'));
            if (twitterUrl) {
                twitterLink = `
                    <div class="social-container">
                        <a href="${twitterUrl}" target="_blank" rel="noreferrer">
                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 22 22" class="h-20 w-20">
                                <path d="m12.912 9.502 7.85-9.124h-1.86L12.084 8.3 6.642.378H.362l8.233 11.98-8.232 9.569h1.86l7.198-8.367 5.749 8.367h6.278L12.911 9.502Zm-2.548 2.962-.834-1.193-6.637-9.493H5.75l5.356 7.661.834 1.193 6.962 9.958h-2.857l-5.681-8.126Z" fill="currentColor"></path>
                            </svg>
                        </a>
                    </div>`;
            }
            const linkedinUrl = person.sameAs.find(url => url.includes('linkedin.com'));
            if (linkedinUrl) {
                linkedinLink = `
                    <div class="social-container">
                        <a href="${linkedinUrl}" target="_blank" rel="noreferrer">
                            <img src="https://ti-assets.theinformation.com/packs/static/assets/images/linkedin_black-c57c74e581aeee118000.svg" alt="LinkedIn logo">
                        </a>
                    </div>`;
            }
            const instagramUrl = person.sameAs.find(url => url.includes('instagram.com'));
            if (instagramUrl) {
                instagramLink = `
                    <div class="social-container">
                        <a href="${instagramUrl}" target="_blank" rel="noreferrer">
                            <img src="https://ti-assets.theinformation.com/packs/static/assets/images/instagram_black-d4024b0f2e939d8d4a18.svg" alt="Instagram logo">
                        </a>
                    </div>`;
            }
        }
        const location = person.address || '';
        const locationHtml = location ? `
            <a class="location-wrapper" href="/directory?location=${encodeURIComponent(location)}">
                <img src="https://ti-assets.theinformation.com/packs/static/assets/images/directory/location_icon_grey-34cc69ba964d4c7025b9.svg" role="presentation">
                <div>${location}</div>
            </a>` : '';
        const container = document.querySelector('.ProfileContent') || document.body;
        if (!container) return;
        const profileDiv = document.createElement('div');
        profileDiv.id = 'Subscriber';
        profileDiv.className = 'container';
        let html = `
            <div class="profile-start row">
                <div class="col-sm-12 col-md-8 w-full sm:w-auto col">
                    <div class="profile-image-wrapper">
                        <div class="profile-img">
                            <img src="${image}" alt="${name}">
                        </div>
                        <div class="profile-header-detail">
                            <div class="profile-header">
                                <h1 class="name-container">${name}</h1>
                            </div>
                            <div class="profile-header-wrapper">
                                <div class="profile-subheader">
                                    <div>Staff Member</div>
                                    <div class="dot-middle">.</div>
                                    <a href="/directory?ai_query=works%20for%20%22The%20Information%22">The Information</a>
                                </div>
                                <div class="profile-subheader">Subscriber since 2013</div>
                                <div class="social-contact-container" id="profile-seo-container">
                                    <div class="social-contact-wrapper">
                                        ${twitterLink}
                                        ${linkedinLink}
                                        ${instagramLink}
                                        ${url ? `
                                            <div class="social-container">
                                                <a href="${url}" target="_blank" rel="noreferrer">
                                                    <img src="https://ti-assets.theinformation.com/packs/static/assets/images/link_black-833a178216f9aad547f0.svg" role="presentation">
                                                </a>
                                            </div>` : ''}
                                    </div>
                                    ${locationHtml}
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="ProfileContent container">
                <div class="row">
                    <div class="col-sm-12 col-md-2 profile-detail-nav col">
                        <ul class="profile-nav">
                            <li><a class="link-nav"><div class="profile-nav-icon"><svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-24"><path d="M12 0C5.381 0 0 5.381 0 12s5.381 12 12 12 12-5.381 12-12S18.619 0 12 0Zm0 6.872a3.774 3.774 0 0 1 3.764 3.764A3.774 3.774 0 0 1 12 14.4a3.774 3.774 0 0 1-3.764-3.764A3.774 3.774 0 0 1 12 6.872ZM5.432 19.276v-.556a2.906 2.906 0 0 1 2.905-2.905h7.326a2.905 2.905 0 0 1 2.905 2.905v.556A9.795 9.795 0 0 1 12 21.802a9.795 9.795 0 0 1-6.568-2.526Z" fill="currentColor"></path></svg></div>About</a></li>
                            <li><a class="link-nav"><div class="profile-nav-icon"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="20" fill="none" class="w-18 h-20"><path fill="currentColor" d="M16 0H3C1.794 0 0 .799 0 3v14c0 2.201 1.794 3 3 3h15v-2H3.012C2.55 17.988 2 17.805 2 17c0-.101.009-.191.024-.273.112-.575.583-.717.987-.727H17c.018 0 .031-.009.049-.01H18V2c0-1.103-.897-2-2-2Zm0 14H2V3c0-.806.55-.988 1-1h7v7l2-1 2 1V2h2v12Z"></path></svg></div>Articles</a></li>
                            <li><a class="link-nav"><div class="profile-nav-icon"><svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" class="w-[26px]"><path d="M25.15 4.52v-.087l-.076-.043-6.19-3.52-.074-.042-.075.042-5.725 3.257L7.283.87 7.208.827 7.134.87.944 4.41l-.076.044v.087L.85 18.18v.087l.076.043 12.008 6.82.074.043.075-.043 6.19-3.521.076-.043V14.969l5.725-3.278.076-.044V4.52Zm-22.682.231 4.741-2.696 4.657 2.649h-.002l.084.048-4.74 2.696-4.74-2.697Zm15.805 16.2-4.725 2.688v-5.37l4.725-2.689v5.371ZM14.09 4.741l4.722-2.686 4.704 2.675-4.722 2.686-4.704-2.675ZM7.747 8.37l4.725-2.687v5.35l-4.725 2.687V8.37Zm5.245 8.946L8.307 14.65l4.685-2.665 4.737 2.676-4.737 2.654ZM24.074 5.66v5.37l-4.726 2.689V8.349l4.726-2.688Z" fill="currentColor" stroke="currentColor" stroke-width=".3"></path></svg></div>Topics</a></li>
                        </ul>
                    </div>
                    <div class="col-sm-12 col-md-10 profile-detail-wrapper col">
                        <div class="section-container" id="About">
                            <div class="section-header">About</div>
                            <div class="section-detail">${description.replace(/\r\n/g, ' ')}</div>
                        </div>
                        <div class="section-container" id="Articles">
                            <div class="section-header">Articles</div>
                            <div class="section-detail">
                                <div>
        `;
        articles.forEach((article, index) => {
            const articleImage = article.image || 'https://tii.imgix.net/production/articles/article_default_rebrand.jpg?auto=format&w=250';
            const date = new Date(article.datePublished).toLocaleString('en-US', {
                year: 'numeric', month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit', timeZoneName: 'short'
            });
            html += `
                <div class="zHO_NLr7ewdHOaxGnkLl">
                    <div class="sfDgx1CT0jYy_F44fvMz" data-test="article-or-briefing-feed-item">
                        <div class="ost5fDu0w_utvvWjnhwA">
                            <div class="WGoBkCwesGqC7Fvf6poD"></div>
                            <a href="${article.url}" class="no-link track-click article-link relative" id="article-${index}" data-type="link" data-name="article title" data-location="feed">
                                <div class="y4HMnE6OwEbmQz2QhBGZ">${article.headline}</div>
                            </a>
                            <div class="RlSVo1yyINvbwsj5E850">By <span><a href="${url}">${name}</a></span><span> · ${date}</span></div>
                            <div class="k70eEce7kcJDsOPtef9D"><a href="${article.url}">${article.headline}...</a></div>
                        </div>
                        <div class="iq1CRsBjkqjCqMKyrNRQ">
                            <a href="${article.url}" class="track-link relative" id="article-image-${index}" data-type="link" data-name="article image" data-location="feed">
                                <img class="article-image max-xs:max-w-full" width="270" height="151.875" src="${articleImage}">
                            </a>
                        </div>
                    </div>
                </div>
            `;
        });
        html += `
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        `;
        profileDiv.innerHTML = html;
        if (container.classList.contains('ProfileContent')) {
            container.innerHTML = profileDiv.querySelector('.ProfileContent').innerHTML;
        } else {
            container.insertBefore(profileDiv, container.firstChild);
        }
    }
    function loadAndRenderProfile() {
        const scriptTags = document.querySelectorAll('script[type="application/ld+json"]');
        for (const scriptTag of scriptTags) {
            try {
                const data = JSON.parse(scriptTag.textContent);
                if (data['@type'] === 'ProfilePage' && data.mainEntity) {
                    renderProfile(data);
                    break;
                }
            } catch (e) {}
        }
    }
    function parseGoogleItem(item) {
        return {
            title: item.querySelector('title')?.textContent || '',
            url: item.querySelector('link')?.textContent || '',
            source: item.querySelector('source')?.textContent || 'Unknown',
            date: item.querySelector('pubDate')?.textContent || ''
        };
    }
    function parseBingItem(item) {
        const description = item.querySelector('description')?.textContent || '';
        const actualUrlMatch = description.match(/href="([^"]+)"/);
        const actualUrl = actualUrlMatch ? actualUrlMatch[1] : item.querySelector('link')?.textContent || '';
        const publication = item.getElementsByTagName('news:publication')[0];
        const source = publication?.getElementsByTagName('news:name')[0]?.textContent || 'Unknown';
        return {
            title: item.querySelector('title')?.textContent || '',
            url: actualUrl,
            source: source,
            date: item.querySelector('pubDate')?.textContent || ''
        };
    }
    function normalizeUrl(url) {
        try {
            const urlObj = new URL(url);
            return urlObj.hostname + urlObj.pathname;
        } catch (e) {
            return url;
        }
    }
    function fetchRss(url, parseItem) {
        return new Promise(resolve => {
            GM.xmlHttpRequest({
                method: 'GET',
                url: url,
                onload: function(response) {
                    if (response.status === 200) {
                        const xmlText = response.responseText;
                        const parser = new DOMParser();
                        const xmlDoc = parser.parseFromString(xmlText, 'text/xml');
                        const items = xmlDoc.querySelectorAll('item');
                        const parsedArticles = Array.from(items).map(parseItem);
                        resolve({ articles: parsedArticles, error: null });
                    } else {
                        resolve({ articles: [], error: `Error loading articles from ${url}` });
                    }
                },
                onerror: function(error) {
                    resolve({ articles: [], error: error.message });
                }
            });
        });
    }
    function linkTwitterHandles(element) {
        const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, false);
        const textNodes = [];
        let node;
        while (node = walker.nextNode()) {
            let parent = node.parentNode;
            let insideAnchor = false;
            while (parent && parent !== element) {
                if (parent.tagName === 'A') {
                    insideAnchor = true;
                    break;
                }
                parent = parent.parentNode;
            }
            if (!insideAnchor) {
                textNodes.push(node);
            }
        }
        textNodes.forEach(textNode => {
            const text = textNode.textContent;
            const regex = /(?<!\w)@(\w{1,15})\b/g;
            let match;
            let lastIndex = 0;
            const fragments = [];
            while ((match = regex.exec(text)) !== null) {
                const before = text.substring(lastIndex, match.index);
                if (before) {
                    fragments.push(document.createTextNode(before));
                }
                const handle = match[0];
                const username = match[1];
                const a = document.createElement('a');
                a.href = `https://twitter.com/${username}`;
                a.target = '_blank';
                a.rel = 'noopener noreferrer';
                a.textContent = handle;
                fragments.push(a);
                lastIndex = regex.lastIndex;
            }
            if (lastIndex < text.length) {
                fragments.push(document.createTextNode(text.substring(lastIndex)));
            }
            if (fragments.length > 0) {
                const parent = textNode.parentNode;
                fragments.forEach(fragment => {
                    parent.insertBefore(fragment, textNode);
                });
                parent.removeChild(textNode);
            }
        });
    }
    const pathname = window.location.pathname;
    if (pathname.startsWith('/articles/')) {
        const pageTitle = document.title;
        const articleTitle = pageTitle.split(' | ')[0];
        const googleRssUrl = `https://news.google.com/rss/search?q=${encodeURIComponent(articleTitle)}`;
        const bingRssUrl = `https://www.bing.com/news/search?q=${encodeURIComponent(articleTitle)}&format=rss`;
        const container = document.createElement('div');
        container.style.cssText = `
            position: fixed;
            top: 150px;
            right: 10px;
            width: 300px;
            max-height: 80vh;
            overflow-y: auto;
            background: white;
            padding: 15px;
            font-family: Arial, sans-serif;
            z-index: 9999;
        `;
        container.innerHTML = '<h3 style="margin: 0 0 15px 0; color: #000; font-size: 18px; font-weight: bold;">Alternative Articles</h3><p style="margin: 0; color: #666;">Loading...</p>';
        document.body.appendChild(container);
        Promise.all([
            fetchRss(googleRssUrl, parseGoogleItem),
            fetchRss(bingRssUrl, parseBingItem)
        ]).then(results => {
            const allArticles = results.flatMap(result => result.articles);
            const errors = results.map(result => result.error).filter(Boolean);
            const filteredArticles = allArticles.filter(article =>
                !article.url.toLowerCase().includes('theinformation.com') &&
                !article.source.toLowerCase().includes('the information')
            );
            const uniqueArticles = Array.from(new Map(filteredArticles.map(article => [normalizeUrl(article.url), article])).values());
            uniqueArticles.sort((a, b) => {
                const dateA = a.date ? new Date(a.date) : new Date(0);
                const dateB = b.date ? new Date(b.date) : new Date(0);
                return dateB - dateA;
            });
            const topArticles = uniqueArticles.slice(0, 10);
            if (topArticles.length > 0) {
                const articlesHtml = topArticles.map(article => `
                    <div style="margin-bottom: 20px;">
                        <a href="${article.url}" target="_blank" style="display: block; color: #000; font-weight: bold; font-size: 16px; text-decoration: none; margin-bottom: 5px;">${article.title}</a>
                        <small style="color: #666; font-size: 12px;">${article.source} - ${article.date ? new Date(article.date).toLocaleDateString() : 'Unknown date'}</small>
                    </div>
                `).join('');
                container.innerHTML = `
                    <h3 style="margin: 0 0 15px 0; color: #000; font-size: 18px; font-weight: bold;">Alternative Articles <a href="#" style="float: right; font-size: 14px; color: #666; text-decoration: none;" id="close-alternatives">✕</a></h3>
                    ${articlesHtml}
                `;
            } else {
                container.innerHTML = '<h3 style="margin: 0 0 15px 0; color: #000; font-size: 18px; font-weight: bold;">Alternative Articles</h3><p style="margin: 0; color: #666;">No articles found.</p>';
            }
            container.querySelector('#close-alternatives')?.addEventListener('click', (e) => {
                e.preventDefault();
                container.style.display = 'none';
            });
            if (errors.length > 0) {
                const errorHtml = errors.map(error => `<p style="margin: 15px 0 10px 0; color: #666;">${error}</p>`).join('');
                container.insertAdjacentHTML('beforeend', errorHtml);
            }
        });
    }
    window.addEventListener('load', () => {
        if (location.pathname.startsWith('/projects/')) {
            const dataset = extractDataset();
            if (dataset) createPremiumModal(dataset);
        }
        if (location.pathname.startsWith('/u')) {
            loadAndRenderProfile();
            setTimeout(loadAndRenderProfile, 1800);
        }
        const scriptTag = document.querySelector('script[type="application/json"][data-component-name="Article"]');
        if (scriptTag) {
            let jsonData;
            try {
                jsonData = JSON.parse(scriptTag.textContent);
            } catch (e) {
                return;
            }
            const displayDiv = document.createElement('div');
            displayDiv.className = 'article-display';
            if (jsonData.article.picture && jsonData.article.id) {
                const img = document.createElement('img');
                img.src = `https://tii.imgix.net/production/articles/${jsonData.article.id}/${jsonData.article.picture}?auto=compress&fit=crop&auto=format`;
                img.alt = jsonData.article.title || 'Article Picture';
                img.className = 'article-picture';
                img.setAttribute('loading', 'lazy');
                displayDiv.appendChild(img);
                if (jsonData.article.pictureCaption) {
                    const captionP = document.createElement('p');
                    captionP.textContent = jsonData.article.pictureCaption;
                    captionP.style.fontStyle = 'italic';
                    displayDiv.appendChild(captionP);
                }
            }
            const titleH1 = document.createElement('h1');
            titleH1.textContent = jsonData.article.title || 'Untitled Article';
            if (jsonData.article.highlight && jsonData.article.highlight.startsWith('Exclusive:')) {
                const exclusiveSpan = document.createElement('span');
                exclusiveSpan.textContent = ' (Exclusive)';
                exclusiveSpan.className = 'exclusive';
                titleH1.appendChild(exclusiveSpan);
            }
            displayDiv.appendChild(titleH1);
            const publishedP = document.createElement('p');
            const publishedAt = jsonData.article.publishedAt;
            let publishedText = 'Published: ';
            if (publishedAt) {
                const publishedDate = new Date(publishedAt);
                publishedText += isNaN(publishedDate.getTime())
                    ? publishedAt
                    : publishedDate.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
            } else {
                publishedText += 'Not available';
            }
            publishedP.textContent = publishedText;
            publishedP.style.color = '#666';
            displayDiv.appendChild(publishedP);
            if (jsonData.article.highlight) {
                const highlightP = document.createElement('p');
                let highlightText = jsonData.article.highlight;
                if (highlightText.startsWith('Exclusive:')) {
                    highlightText = '<span class="exclusive">Exclusive:</span>' + highlightText.slice(10);
                }
                highlightP.innerHTML = highlightText;
                highlightP.style.fontStyle = 'italic';
                displayDiv.appendChild(highlightP);
            }
            if (jsonData.article.authors && jsonData.article.authors.length > 0) {
                const authorsDiv = document.createElement('div');
                authorsDiv.className = 'authors';
                jsonData.article.authors.forEach(author => {
                    const authorDiv = document.createElement('div');
                    authorDiv.className = 'author';
                    if (author.picture) {
                        const imgLink = document.createElement('a');
                        imgLink.href = `https://www.theinformation.com/u/${author.username}`;
                        imgLink.target = '_blank';
                        imgLink.rel = 'noopener noreferrer';
                        const img = document.createElement('img');
                        img.src = author.picture;
                        img.alt = author.name || 'Author Picture';
                        img.setAttribute('loading', 'lazy');
                        imgLink.appendChild(img);
                        authorDiv.appendChild(imgLink);
                    }
                    const nameP = document.createElement('p');
                    nameP.innerHTML = `<a href="https://www.theinformation.com/u/${author.username}" target="_blank" rel="noopener noreferrer"><strong>${author.name || 'Unnamed Author'}</strong></a>`;
                    const bioText = author.bio && author.bio.trim() ? author.bio : author.fullBio;
                    if (author.twitter) {
                        const twitterUsername = author.twitter.replace(/^@/, '');
                        if (!bioText || !bioText.includes(`@${twitterUsername}`)) {
                            nameP.innerHTML += ` - <a href="https://twitter.com/${twitterUsername}" target="_blank" rel="noopener noreferrer">@${twitterUsername}</a>`;
                        }
                    }
                    authorDiv.appendChild(nameP);
                    if (bioText) {
                        const bioP = document.createElement('p');
                        bioP.innerHTML = bioText;
                        linkTwitterHandles(bioP);
                        bioP.querySelectorAll('a').forEach(a => {
                            a.target = '_blank';
                            a.rel = 'noopener noreferrer';
                        });
                        authorDiv.appendChild(bioP);
                    }
                    authorsDiv.appendChild(authorDiv);
                });
                displayDiv.appendChild(authorsDiv);
            }
            if (jsonData.article.freeBlurb) {
                const summaryH3 = document.createElement('h3');
                summaryH3.textContent = 'Summary';
                displayDiv.appendChild(summaryH3);
                const summaryDiv = document.createElement('div');
                summaryDiv.className = 'summary';
                summaryDiv.innerHTML = jsonData.article.freeBlurb;
                summaryDiv.querySelectorAll('a').forEach(a => {
                    a.target = '_blank';
                    a.rel = 'noopener noreferrer';
                });
                displayDiv.appendChild(summaryDiv);
            }
            if (jsonData.article.pickups && jsonData.article.pickups.length > 0) {
                displayDiv.appendChild(document.createElement('hr'));
                const pickupsH3 = document.createElement('h3');
                pickupsH3.textContent = jsonData.article.pickups.length > 1 ? 'Sources' : 'Source';
                displayDiv.appendChild(pickupsH3);
                const pickupsUl = document.createElement('ul');
                jsonData.article.pickups.forEach(pickup => {
                    const li = document.createElement('li');
                    li.innerHTML = `<a href="${pickup.url}" target="_blank" rel="noopener noreferrer">${pickup.title}</a> (${pickup.publication})`;
                    pickupsUl.appendChild(li);
                });
                displayDiv.appendChild(pickupsUl);
            }
            if (jsonData.mostPopularArticles && jsonData.mostPopularArticles.length > 0) {
                displayDiv.appendChild(document.createElement('hr'));
                const popularH3 = document.createElement('h3');
                popularH3.textContent = 'Most Popular Articles';
                displayDiv.appendChild(popularH3);
                jsonData.mostPopularArticles.forEach(article => {
                    const articleDiv = document.createElement('article');
                    articleDiv.className = 'related-article';
                    const textDiv = document.createElement('div');
                    textDiv.className = 'text';
                    if (article.highlight) {
                        const highlightP = document.createElement('p');
                        highlightP.className = 'highlight';
                        let highlightText = article.highlight;
                        if (highlightText.startsWith('Exclusive:')) {
                            highlightText = '<span class="exclusive">Exclusive:</span>' + highlightText.slice(10);
                        }
                        highlightP.innerHTML = highlightText;
                        textDiv.appendChild(highlightP);
                    }
                    const titleH4 = document.createElement('h4');
                    titleH4.innerHTML = `<a href="https://www.theinformation.com/articles/${article.slug}" target="_blank" rel="noopener noreferrer">${article.title || 'Untitled'}</a>`;
                    textDiv.appendChild(titleH4);
                    if (article.publishedAt) {
                        const metaP = document.createElement('p');
                        metaP.className = 'meta';
                        const publishedDate = new Date(article.publishedAt);
                        metaP.textContent = isNaN(publishedDate.getTime())
                            ? article.publishedAt
                            : publishedDate.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
                        textDiv.appendChild(metaP);
                    }
                    articleDiv.appendChild(textDiv);
                    displayDiv.appendChild(articleDiv);
                });
            }
            if (jsonData.relatedArticles && jsonData.relatedArticles.length > 0) {
                displayDiv.appendChild(document.createElement('hr'));
                const relatedH3 = document.createElement('h3');
                relatedH3.textContent = 'Related Articles';
                displayDiv.appendChild(relatedH3);
                jsonData.relatedArticles.forEach(article => {
                    const articleDiv = document.createElement('article');
                    articleDiv.className = 'related-article';
                    if (article.highlight && article.highlight.startsWith('Exclusive:')) {
                        articleDiv.classList.add('exclusive-article');
                    }
                    if (article.picture && article.id) {
                        const imgLink = document.createElement('a');
                        imgLink.href = article.url;
                        imgLink.target = '_blank';
                        imgLink.rel = 'noopener noreferrer';
                        const img = document.createElement('img');
                        img.src = `https://tii.imgix.net/production/articles/${article.id}/${article.picture}?auto=compress&fit=crop&auto=format`;
                        img.alt = article.title || 'Related Article';
                        img.setAttribute('loading', 'lazy');
                        imgLink.appendChild(img);
                        articleDiv.appendChild(imgLink);
                    }
                    const textDiv = document.createElement('div');
                    textDiv.className = 'text';
                    if (article.highlight) {
                        const highlightP = document.createElement('p');
                        highlightP.className = 'highlight';
                        let highlightText = article.highlight;
                        if (highlightText.startsWith('Exclusive:')) {
                            highlightText = '<span class="exclusive">Exclusive:</span>' + highlightText.slice(10);
                        }
                        highlightP.innerHTML = highlightText;
                        textDiv.appendChild(highlightP);
                    }
                    const titleH4 = document.createElement('h4');
                    titleH4.innerHTML = `<a href="${article.url}" target="_blank" rel="noopener noreferrer">${article.title || 'Untitled'}</a>`;
                    textDiv.appendChild(titleH4);
                    if (article.authors || article.publishedAt) {
                        const metaP = document.createElement('p');
                        metaP.className = 'meta';
                        let metaText = '';
                        if (article.authors && article.authors.length > 0) {
                            metaText += 'By ' + article.authors.map(a => a.name).join(', ');
                        }
                        if (article.publishedAt) {
                            const publishedDate = new Date(article.publishedAt);
                            metaText += (metaText ? ' • ' : '') + (isNaN(publishedDate.getTime())
                                ? article.publishedAt
                                : publishedDate.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' }));
                        }
                        metaP.textContent = metaText;
                        textDiv.appendChild(metaP);
                    }
                    if (article.excerpt && article.excerpt.long) {
                        const excerptP = document.createElement('p');
                        excerptP.className = 'excerpt';
                        excerptP.textContent = article.excerpt.long;
                        textDiv.appendChild(excerptP);
                    }
                    if (article.commentCount > 0) {
                        const commentsP = document.createElement('p');
                        commentsP.className = 'comments';
                        commentsP.textContent = `${article.commentCount} comment${article.commentCount > 1 ? 's' : ''}`;
                        textDiv.appendChild(commentsP);
                    }
                    articleDiv.appendChild(textDiv);
                    displayDiv.appendChild(articleDiv);
                });
            }
            const buttonsDiv = document.createElement('div');
            buttonsDiv.className = 'json-buttons';
            const jsonButton = document.createElement('button');
            jsonButton.textContent = '{}';
            jsonButton.title = 'Toggle JSON view';
            jsonButton.className = 'json-toggle-btn';
            buttonsDiv.appendChild(jsonButton);
            displayDiv.appendChild(buttonsDiv);
            const jsonDiv = document.createElement('div');
            jsonDiv.className = 'json-data';
            const pre = document.createElement('pre');
            pre.textContent = JSON.stringify(jsonData, null, 2);
            jsonDiv.appendChild(pre);
            displayDiv.appendChild(jsonDiv);
            jsonButton.addEventListener('click', () => {
                const isHidden = jsonDiv.style.display === 'none' || jsonDiv.style.display === '';
                jsonDiv.style.display = isHidden ? 'block' : 'none';
                jsonButton.textContent = isHidden ? 'Hide JSON' : '{}';
            });
            document.body.insertBefore(displayDiv, document.body.firstChild);
        }
    });
})();