AO3: Replace Y/N in works with your name

replaces Y/N and other placeholders in x Reader fic with the name of your choice

目前為 2023-10-16 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         AO3: Replace Y/N in works with your name
// @description  replaces Y/N and other placeholders in x Reader fic with the name of your choice
// @author       escctrl
// @namespace    https://greasyfork.org/en/users/906106-escctrl
// @version      0.3
// @match        https://archiveofourown.org/works/*
// @license      MIT
// @require      https://ajax.googleapis.com/ajax/libs/jquery/3.7.0/jquery.min.js
// @require      https://ajax.googleapis.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js
// @grant        none
// ==/UserScript==

var given_name = "", family_name = "";

// the function to deal with all the configuration - using jQueryUI for dialogs
(function($) {
    'use strict';

    // retrieve localStorage on page load
    if (!localStorage) {
        console.log("The userscript \"AO3: Replace Y/N in works with your name\" terminated early because local storage cannot be accessed");
        return false;
    }
    else loadconfig();

    // if no other script has created it yet, write out a "Userscripts" option to the main navigation
    if ($('#scriptconfig').length == 0) {
        $('#header ul.primary.navigation li.dropdown').last()
            .after(`<li class="dropdown" id="scriptconfig">
                <a class="dropdown-toggle" href="/" data-toggle="dropdown" data-target="#">Userscripts</a>
                <ul class="menu dropdown-menu"></ul>
            </li>`);
    }
    // then add this script's config option to navigation dropdown
    $('#scriptconfig .dropdown-menu').append(`<li><a href="javascript:void(0);" id="opencfg_replaceYN">Replace Y/N</a></li>`);

    // if the background is dark, use the dark UI theme to match
    let dialogtheme = lightOrDark($('body').css('background-color')) == "dark" ? "ui-darkness" : "base";

    // adding the jQuery stylesheet to style the dialog, and fixing the interference of AO3's styling
    $("head").append(`<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/${dialogtheme}/jquery-ui.css">`)
    .append(`<style tyle="text/css">
    #cfgdialog_replaceYN legend {font-size: inherit; height: auto; width: auto; opacity: inherit;}
    #cfgdialog_replaceYN form {box-shadow: revert; cursor:auto;}
    #cfgdialog_replaceYN fieldset {background: revert; box-shadow: revert;}
    #cfgdialog_replaceYN input[type='text'] { position: relative; top: 1px; padding: .4em; width: 95%; }
    #cfgdialog_replaceYN fieldset p { padding-top: 0; padding-left: 0; padding-right: 0; }
    </style>`);

    // the config dialog container
    let cfg = document.createElement('div');
    cfg.id = 'cfgdialog_replaceYN';
    $(cfg).html(`<form>
    <fieldset><legend>Given Name</legend>
        <p>replaces Y/N and G/N in the work text</p>
        <input type="text" name="given" id="given" value="${given_name}" placeholder="your given name">
    </fieldset>
    <fieldset><legend>Family Name</legend>
        <p>replaces F/N, L/N and Y/L/N in the work text</p>
        <input type="text" name="family" id="family" value="${family_name}" placeholder="your family name">
    </fieldset>
    <p>Any changes to this configuration will only take effect after reloading the page!</p>
    <!-- Allow form submission with keyboard without duplicating the dialog button -->
    <input type="submit" tabindex="-1" style="display: none;">
    </form>`);

    // attach it to the DOM so that selections work
    $("body").append(cfg);

    let dialogwidth = parseInt($("body").css("width")); // parseInt ignores letters (px)
    dialogwidth = dialogwidth > 400 ? 400 : dialogwidth * 0.9;

    // initialize the dialog (but don't open it)
    $( "#cfgdialog_replaceYN" ).dialog({
        appendTo: "#main",
        modal: true,
        title: 'Replace Y/N Configuration',
        draggable: true,
        resizable: false,
        autoOpen: false,
        width: dialogwidth,
        position: {my:"center", at: "center top"},
        buttons: {
            Reset: deleteconfig,
            Save: setconfig,
            Cancel: closedialog
        }
    });

    function closedialog() {
        $( "#cfgdialog_replaceYN" ).dialog( "close" );
    }

    // on click of the menu, open the configuration dialog
    $("#opencfg_replaceYN").on("click", function(e) {
        $( "#cfgdialog_replaceYN" ).dialog('open');
    });

    // event triggers if form is submitted with the <enter> key
    $( "#cfgdialog_replaceYN form" ).on("submit", (e)=>{
        e.preventDefault();
        setconfig();
    });

    // functions to deal with the localStorage
    function loadconfig() {
        given_name = localStorage.getItem('script-replaceYN-given') || "";
        family_name = localStorage.getItem('script-replaceYN-family') || "";
    }
    function setconfig() {
        // grab form fields for easier selection later
        let new_given = $(`#cfgdialog_replaceYN form [name="given"]`).val();
        let new_family = $(`#cfgdialog_replaceYN form [name="family"]`).val();
        localStorage.setItem('script-replaceYN-given', new_given);
        localStorage.setItem('script-replaceYN-family', new_family);
        closedialog();
    }
    function deleteconfig() {
        // empties all fields in the form
        $('#cfgdialog_replaceYN form [name]').val("");
        // delete the localStorage
        localStorage.removeItem('script-replaceYN-given');
        localStorage.removeItem('script-replaceYN-family');
        closedialog();
    }

})(jQuery);

// function to run the text replacement on Y/N and [Y/]L/N etc
// sadly this can run only on initial page load - after that the work text has been changed and we wouldn't find the placeholders to replace
function replaceYN() {
    // don't run a replace if no name has been configured
    if (given_name != "" || family_name != "") {
        document.querySelectorAll('#main #chapters .userstuff > *').forEach((p) => {
            p.innerHTML = p.innerHTML
                .replace(/\(Y\/N\)|Y\/N/ig, given_name).replace(/\(G\/N\)|G\/N/ig, given_name).replace(/\(F\/N\)|F\/N/ig, given_name)
                .replace(/\((Y\/)?L\/N\)|(Y\/)?L\/N/ig, family_name);
        });
    }
}

// replace text only when page finished loading
if (document.readyState === 'complete') replaceYN();
else window.addEventListener('load', () => replaceYN());

// helper function to determine whether a color (the background in use) is light or dark
// https://awik.io/determine-color-bright-dark-using-javascript/
function lightOrDark(color) {

    // Variables for red, green, blue values
    var r, g, b, hsp;

    // Check the format of the color, HEX or RGB?
    if (color.match(/^rgb/)) {
        // If RGB --> store the red, green, blue values in separate variables
        color = color.match(/^rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*(\d+(?:\.\d+)?))?\)$/);
        r = color[1];
        g = color[2];
        b = color[3];
    }
    else {
        // If hex --> Convert it to RGB: http://gist.github.com/983661
        color = +("0x" + color.slice(1).replace(color.length < 5 && /./g, '$&$&'));
        r = color >> 16;
        g = color >> 8 & 255;
        b = color & 255;
    }

    // HSP (Highly Sensitive Poo) equation from http://alienryderflex.com/hsp.html
    hsp = Math.sqrt( 0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b) );

    // Using the HSP value, determine whether the color is light or dark
    if (hsp>127.5) { return 'light'; }
    else { return 'dark'; }
}