Cuddlyoctopus Uncensored

Reveal products on cuddlyoctopus.com that were hidden due to censorship, and preview mix-n-match products.

// ==UserScript==
// @name         Cuddlyoctopus Uncensored
// @namespace    http://flatkyubu.com/
// @version      2025-04-08-alpha-001
// @description  Reveal products on cuddlyoctopus.com that were hidden due to censorship, and preview mix-n-match products.
// @author       Flat Kyubu
// @match        https://cuddlyoctopus.com/*
// @grant        GM_addStyle
// ==/UserScript==

(function() {
    'use strict';
    const CO_PAGELOADED_CHECK_REFRESH = 200;
    const CO_PAGELOADED_CHECK_TRIES = 5;

    function co_decensor_product()
    {
        // Add back the buttons to select the censored versions of the products on product pages
        // The buttons are not included on the server, so add them manually.

        let variants = JSON.parse(document.querySelector("form.variations_form")?.getAttribute("data-product_variations")??"[]");
        if (variants.length === 0) {
            return;
        }
        let productId = document.querySelector("form.variations_form").getAttribute('data-product_id');

        let variantHolderDOM = document.querySelector(".attribute-pa_variant").querySelector(".value");
        let existingVariantNames = Array.from(variantHolderDOM.querySelectorAll("div>input"), e => e.getAttribute('value'));
        variants.forEach(variantData => {
            let variantName = variantData.attributes.attribute_pa_variant;
            if (existingVariantNames.includes(variantName)) {
                return;
            }
            let newId = `pa_variant_v_${variantName}${productId}`;
            let newInput = Object.assign(document.createElement('input'), {
                'type': 'radio',
                'name': 'attribute_pa_variant',
                'value': variantName,
                'id': newId,
            });
            let newLabel = document.createElement('label');
            newLabel.setAttribute('for', newId);
            let newLabelText = document.createElement('span');
            newLabelText.innerText = `👁️‍🗨️ ${variantName}`;
            newLabel.appendChild(newLabelText);
            let newSelector = document.createElement('div');
            newSelector.appendChild(newInput);
            newSelector.appendChild(newLabel);
            variantHolderDOM.appendChild(newSelector);
        });
    }

    function co_inject_mixnmatch_styles() {
        GM_addStyle(`
        .co-mixnmatchplus-wrapper {
            float: left;
            position: relative;
        }
        .co-mixnmatchplus-originalimg {
            width: 100%;
            height: auto;
            max-width: 100%;
        }
    `);
    }


    function co_show_mixnmatch_cart(retries = 0)
    {
        let cartDOM = document.querySelector('.wc-block-cart-items');
        if (cartDOM == null) {
            if (retries < CO_PAGELOADED_CHECK_TRIES) {
                setTimeout(() => co_show_mixnmatch_cart(retries + 1), CO_PAGELOADED_CHECK_REFRESH);
            }
            return;
        }

        co_inject_mixnmatch_styles();
        let productsInCartDOM = document.querySelectorAll('.wc-block-cart-items__row');

        productsInCartDOM.forEach(productDOM => {
            let productName = productDOM.querySelector('.wc-block-components-product-name').innerText;
            if (productName !== 'Mix-and-Match') {
                return;
            }
            let productDetailsDOM = productDOM.querySelector('.wc-block-components-product-metadata');
            let side1skus = productDetailsDOM.querySelector(
                '.wc-block-components-product-details__side-1 .wc-block-components-product-details__value').innerText;
            let side2skus = productDetailsDOM.querySelector(
                '.wc-block-components-product-details__side-2 .wc-block-components-product-details__value').innerText;
            let side1uri = `https://cuddlyoctopus.com/wp-content/uploads/daki-thumbnails/${side1skus}.jpg`;
            let side2uri = `https://cuddlyoctopus.com/wp-content/uploads/daki-thumbnails/${side2skus}.jpg`;
            let productImageDOM = productDOM.querySelector('.wc-block-cart-item__image');

            let wrapperDOM = document.createElement('div');
            wrapperDOM.className = "co-mixnmatchplus-wrapper";
            let image1DOM = document.createElement('img');
            image1DOM.src = side1uri;
            image1DOM.classList.add('mix-preview', 'mix-a');
            let image2DOM = document.createElement('img');
            image2DOM.src = side2uri;
            image2DOM.classList.add('mix-preview', 'mix-b');
            let existingImageDOM = productImageDOM.querySelector('img');
            existingImageDOM.classList.add('co-mixnmatchplus-originalimg');

            wrapperDOM.appendChild(image2DOM);
            wrapperDOM.appendChild(image1DOM);
            wrapperDOM.appendChild(existingImageDOM);
            productImageDOM.appendChild(wrapperDOM);
        });
    }

    function co_show_mixnmatch_wishlist(retries=0)
    {
        let wishlistDOMcheck = document.querySelector('.wishlist_item');
        if (wishlistDOMcheck == null) {
            if (retries < CO_PAGELOADED_CHECK_TRIES) {
                setTimeout(() => co_show_mixnmatch_wishlist(retries+1), CO_PAGELOADED_CHECK_REFRESH);
            }
            return;
        }

        co_inject_mixnmatch_styles();
        let productsInCartDOM = document.querySelectorAll('.wishlist_item');

        productsInCartDOM.forEach(productDOM => {
            let productDetailsDOM = productDOM.querySelector('.product-name');
            if (productDetailsDOM.firstChild?.nodeValue?.trim() !== 'Mix-and-Match') {
                return;
            }
            let side1skus = productDetailsDOM.querySelector('dd.variation-mix-side-a').innerText;
            let side2skus = productDetailsDOM.querySelector('dd.variation-mix-side-b').innerText;
            let side1uri = `https://cuddlyoctopus.com/wp-content/uploads/daki-thumbnails/${side1skus}.jpg`;
            let side2uri = `https://cuddlyoctopus.com/wp-content/uploads/daki-thumbnails/${side2skus}.jpg`;
            let productImageDOM = productDOM.querySelector('.kw-prodimage');

            let wrapperDOM = document.createElement('div');
            wrapperDOM.className = "co-mixnmatchplus-wrapper";
            let image1DOM = document.createElement('img');
            image1DOM.src = side1uri;
            image1DOM.classList.add('mix-preview', 'mix-a');
            let image2DOM = document.createElement('img');
            image2DOM.src = side2uri;
            image2DOM.classList.add('mix-preview', 'mix-b');
            let existingImageDOM = productImageDOM.querySelector('img');
            existingImageDOM.classList.add('co-mixnmatchplus-originalimg');

            wrapperDOM.appendChild(image2DOM);
            wrapperDOM.appendChild(image1DOM);
            wrapperDOM.appendChild(existingImageDOM);
            productImageDOM.appendChild(wrapperDOM);
        });
    }

    function co_decensor_shop()
    {
        // Two concerns here:
        // 1. Show products that were unlisted completely as a result of censorship.
        //   Some products have been completely removed (thier original link now gives a 404), eg the original racing miku.
        //   However they are still accessible if saved to the wishlist.
        //   Similarly some blankets/tapestries are completely missing.
        // 2. Show the nsfw variants of products in the listing page when nsfw is turned on.

        // Currently, I don't see how to do either easily without making our own database.
        // Perhaps some JSON data embedded in this script would work.
    }


    function co_fix_mixnmatch_page()
    {
        // Two issues that need fixed:
        // 1. Some images don't load because they aren't available at the requested URLs on the server
        // 2. 'add to wishlist' is broken if you already have any mixnmatch added

        // Again we might need our own data lookup for (1), and more research is needed for (2).
    }

    function is_all_ages()
    {
        return document.querySelector('#sfwSwitcher .sfw-btn')?.classList?.contains('aa-on') ?? true;
    }

    function co_decensor()
    {
        if (!!location.href.startsWith("https://cuddlyoctopus.com/product/mix-and-match")) {
            co_fix_mixnmatch_page();
        }
        else if (!!location.href.startsWith("https://cuddlyoctopus.com/product")) {
            if (!is_all_ages()) {
                co_decensor_product();
            }
        }
        else if (!!location.href.startsWith("https://cuddlyoctopus.com/shop")) {
            if (!is_all_ages()) {
                co_decensor_shop();
            }
        }
        else if (!!location.href.startsWith("https://cuddlyoctopus.com/cart")) {
            co_show_mixnmatch_cart();
        }
        else if (!!location.href.startsWith("https://cuddlyoctopus.com/wishlist")) {
            co_show_mixnmatch_wishlist();
        }
    }

    co_decensor();
})();