[Iltalehti] IltaPlus

Iltalehden "Plus-Tilaus" ilmaiseksi

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.

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

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        [Iltalehti] IltaPlus
// @namespace   HKR
// @match       https://www.iltalehti.fi/*
// @grant       none
// @version     2.2
// @author      HKR
// @description Iltalehden "Plus-Tilaus" ilmaiseksi
// @run-at      document-load
// @grant       GM_addStyle
// ==/UserScript==

class ElementGenerator {
    handleType(type, sectionData) {
        switch(type) {
            case "paragraph":
                return this.paragraph(sectionData.items);
            case "subheadline":
                return this.subheadline(sectionData.text);
            case "list":
                return this.list(sectionData.items);
            case "aside":
                return this.aside(sectionData.items);
            case "related-article":
                return this.relatedArticle(sectionData.article);
            case "image":
                return this.image(sectionData);
            case "directImage":
                return this.directImage(sectionData);
            case "embed":
                return this.embed.unknown(sectionData);
            case "divider":
                return this.divider();
        }
    }

    content(contentArr) {
        let generatedHTML = "";

        contentArr.forEach(item => {
            const type = item.type;

            switch(type) {
                case "text":
                    generatedHTML += `${item.text}`;
                    break;

                case "bold":
                    let boldText = "";

                    item.items.forEach(boldItem => {
                        boldText += boldItem.text;
                    });

                    generatedHTML += `<strong>${boldText}</strong>`;
                    break;

                case "link":
                    let generatedContent = this.content(item.items);

                    generatedHTML += `<a href="${item.url}">${generatedContent}</a>`;
                    break;

                case "italic":
                    let italicText = "";

                    item.items.forEach(item => {
                        italicText += item.text;
                    });

                    generatedHTML += `<i>${italicText}</i>`;
                    break;

                default:
                    let text= "";

                    item.items.forEach(item => {
                        text += item.text;
                    });

                    generatedHTML += text;
                    break;
            }
        });

        return generatedHTML;
    }

    paragraph(contentArr) {
        let paragraph = document.createElement('p');
        paragraph.className = "paragraph";

        let generatedHTML = this.content(contentArr);

        paragraph.innerHTML = generatedHTML;

        return paragraph;
    }

    subheadline(textContent) {
        const subheadline = document.createElement('h3');
        subheadline.className = "subheadline";
        subheadline.innerText = textContent;

        return subheadline;
    }

    image(imageData) {
        const image = document.createElement('div');
        image.className = `article-image gallery ${imageData.properties.float}`;
        image.setAttribute("itemprop", "image");
        image.setAttribute("itemscope", "");
        image.setAttribute("itemtype", "http://schema.org/ImageObject");
        image.setAttribute("role", "button");
        image.setAttribute("tabindex", "0");
        image.innerHTML = `
        <div class="article-image-container" style="padding-bottom:${(imageData.properties.height / imageData.properties.width) * 100}%">
            <img class="image image-show image-preview" src="${imageData.urls.size30}" alt="${imageData.properties.caption}">
            <img class="image image-show" src="${imageData.urls.size612}" srcset="${imageData.urls.size310} 310w, ${imageData.urls.size510} 510w, ${imageData.urls.size612} 612w" alt="${imageData.properties.caption}">
        </div>
        <meta itemprop="url" content="${imageData.urls.size612}">
        <meta itemprop="width" content="${imageData.properties.width}">
        <meta itemprop="height" content="${imageData.properties.height}">
        <div class="media-caption">
            <meta itemprop="description" content="${imageData.properties.caption}"/>
            <span class="caption-text" itemprop="description">${imageData.properties.caption}</span>
            <span class="media-source">${imageData.properties.source}</span>
        </div>
        `;

        return image;
    }

    directImage(imageData) {
        const image = document.createElement('div');
        image.className = `article-image gallery ${imageData.float}`;
        image.setAttribute("itemprop", "image");
        image.setAttribute("itemscope", "");
        image.setAttribute("itemtype", "http://schema.org/ImageObject");
        image.setAttribute("role", "button");
        image.setAttribute("tabindex", "0");
        image.innerHTML = `
        <div class="article-image-container" style="padding-bottom:${(imageData.height / imageData.width) * 100}%">
            <img class="image image-show image-preview" src="${imageData.urls.size30}" alt="${imageData.caption}">
            <img class="image image-show" src="${imageData.urls.size612}" srcset="${imageData.urls.size310} 310w, ${imageData.urls.size510} 510w, ${imageData.urls.size612} 612w" alt="${imageData.caption}">
        </div>
        <meta itemprop="url" content="${imageData.urls.size612}">
        <meta itemprop="width" content="${imageData.width}">
        <meta itemprop="height" content="${imageData.height}">
        <div class="media-caption">
            <meta itemprop="description" content="${imageData.caption}"/>
            <span class="caption-text" itemprop="description">${imageData.caption}</span>
            <span class="media-source">${imageData.source}</span>
        </div>
        `;

        return image;
    }

    list(items) {
        const listElem = document.createElement('div');
        listElem.className = "article-bullets";

        const list = document.createElement('ul');

        items.forEach(item => {
            let generatedHTML = this.content(item);

            list.innerHTML += `<li>${generatedHTML}</li>`;
        });

        listElem.appendChild(list);

        return listElem;
    }

    aside(items) {
        const asideContainer = document.createElement('div');
        asideContainer.className = "aside-container";

        const aside = document.createElement('div');
        aside.className = "aside";

        items.forEach(item => {
            const type = item.type;

            const sectionElement = this.handleType(type, item);

            if(typeof sectionElement == "object") {
                aside.appendChild(sectionElement);
            }
        });

        asideContainer.appendChild(aside);

        return asideContainer;
    }

    relatedArticle(article) {
        const relatedArticle = document.createElement('div');
        relatedArticle.className = "related-articles related-articles-within-text";

        relatedArticle.innerHTML += `<h3>Lue myös</h3>`;
        relatedArticle.innerHTML += `<a href="/${article.category.category_name}/a/${article.article_id}">${article.headline}</a>`;

        return relatedArticle;
    }

    divider() {
        const divider = document.createElement('div');
        divider.className = "article-divider";
        divider.innerHTML = `<div class="article-divider-content"></div>`;

        return divider;
    }

    embed = {
        twitter: twitterData => {
            const twitterEmbed = document.createElement('div');
            twitterEmbed.className = "twitter-container article-embed";
            twitterEmbed.innerHTML = twitterData.embed_html;

            return twitterEmbed;
        },
        default: embedData => {
            const embed = document.createElement('div');
            embed.innerHTML = embedData.embed_html;

            return embed.firstChild;
        },
        unknown: embedData => {
            switch(embedData.name) {
                case "twitter": return this.embed.twitter(embedData);
                default: return this.embed.default(embedData);
            }
        }
    }
};

const bypassPaywall = async () => {
    const page = digitalData.page.attributes;
    const pageID = page.content.cid;
    const paidArticle = page.contentCharge == "paid" ? true : false;

    if(!paidArticle) return; // article is free, do not proceed

    const articleBodyElement = document.createElement('div');
    articleBodyElement.id = "bypassed-article-body";
    articleBodyElement.innerHTML = `
    <div style="margin-bottom: 10px;">
        <a class="subheadline" href="https://github.com/Hakorr/Userscripts/tree/main/Iltalehti.fi/IltaPlus">
            Bypassed by <strong>IltaPlus</strong>
        </a>
    </div>
    `;

    const articleData = await fetch(`https://api.il.fi/v1/articles/${pageID}?include_main_media=true`)
        .then(response => response.json())
        .then(json => json.response);

    console.log("Article data: ", articleData);

    const articleBody = articleData.body;
    const articleImages = articleData.ímages;

    const ElemGen = new ElementGenerator();

    articleBody.forEach(sectionData => {
        const type = sectionData.type;

        const sectionElement = ElemGen.handleType(type, sectionData);

        if(typeof sectionElement == "object") {
            articleBodyElement.appendChild(sectionElement);
        }
    });

    articleData?.["images"].forEach(sectionData => {
        const sectionElement = ElemGen.handleType("directImage", sectionData);

        if(typeof sectionElement == "object") {
            articleBodyElement.appendChild(sectionElement);
        }
    });

    GM_addStyle(`
    .subheadline {
         font-size: 18px; margin: 0 14px 15px;
    }
    .aside-container {
        clear: both;
        font-size: 16px;
        margin: 0 14px 14px;
        border: 1px solid #ddd;
    }
    .aside-container h3:first-of-type {
        margin: 14px;
    }
    .aside-container h3 {
        font-family: Bernino Sans Condensed,Arial,Verdana;
        font-size: 16px;
        font-weight: 700;
        margin: 10px 14px;
    }
    .paragraph {
        margin: 0 14px 15px;
        line-height: 1.45;
    }
    .media-source {
      opacity: 0.5;
    }
    `);

    console.log("Processed (n' bypassed) article:", articleBodyElement);

    document.querySelector(".article-body").innerHTML = articleBodyElement.innerHTML;
    document.querySelector("#anop-container")?.remove();
};

let lastID = "";

setInterval(() => {
    let currentID = digitalData.page.attributes.content.cid;

    if(lastID != currentID)
    {
        lastID = currentID;
        bypassPaywall();
    }
}, 500);