AO3: [Wrangling] Synonym Autofill

Adds buttons to relationship edit tag pages to autofill synonym field from tagged characters

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         AO3: [Wrangling] Synonym Autofill
// @description  Adds buttons to relationship edit tag pages to autofill synonym field from tagged characters
// @version      1.0

// @author       Nexidava
// @namespace    https://greasyfork.org/en/users/725254

// @match        *://*.archiveofourown.org/tags/*/edit
// @require      https://ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js
// @grant        none
// @license      GPL-3.0 <https://www.gnu.org/licenses/gpl.html>
// ==/UserScript==

class Tag {
    constructor(tag, western) {
        this.tag = tag
        const match = tag.match(/(.*?) ?(\(.*\))?\s?$/)
        this.name = match[1]
        this.disambig = match[2]
        this.western = western
        this.order = this.getOrder()
    }
    getOrder() {
        var order = this.name
        if (this.western) {
            // put the last name first.  this will unavoidably break with multiple space-separated last names, but does handle middle names
            order = order.split(" | ").map(name => name.split(" ")).map(words => words.slice(-1).concat(words.slice(0,-1)).join(" ")).join(" | ")
        }
        if (this.disambig) {
            order += " " + this.disambig
        }
        return order
    }
}

function autofill($, western) {
    // get character tags
    const charbox = $("dd[title=Characters].tags.listbox.group")
    const taglist = charbox.find("a.tag, li.added.tag")
    const charobj = taglist.map(function(t) { return $(this).contents().filter(function() { return this.nodeType === 3; }).text().trim() })
    var charr = $.makeArray(charobj)
    const name = $("input#tag_name").val()

    // if no chars tagged, get characters from tag
    if (!charr.length) {
        charr = name.split(/\s?[/&]\s?/g)
    }

    // construct and sort char objects
    var chars = charr.map(x => new Tag(x, western))
    chars.sort((a, b) => a.order.localeCompare(b.order))

    // get rel type from tag name
    var sep
    if (name.match(/\//g)) { sep = "/" }
    else if (name.match(/&/g)) { sep = " & " }
    else { sep = " # " ; console.log("Unable to intuit correct rel type, using #") }

    // construct rel tag
    const hasdis = chars.map(x => x.disambig).filter(Boolean)
    const ndis = (new Set(hasdis)).size
    const keep_disambig = $("input[id='keep_disambig']:checked").length === 1
    var tag

    if (ndis == 1 && !keep_disambig) {
        tag = chars.map(x => x.name).join(sep)

        if (hasdis.length == chars.length) {
            tag += " " + chars[0].disambig
        }
    }
    else {
        tag = chars.map(x => x.tag).join(sep)
    }

    var canonical_checkbox = $("input#tag_canonical")
    var syn_autocomplete = $("input#tag_syn_string_autocomplete")

    // if computed tag matches existing name, tick canonical box instead of autofilling
    if (tag === name) {
        canonical_checkbox.prop('checked', true)
        syn_autocomplete.val("")
    }
    // else input rel tag to synonym field and uncheck canonical checkbox if needed
    else {
        canonical_checkbox.prop('checked', false)
        syn_autocomplete.val(tag)
    }
}

// set up buttons
(function($) {
    // only function on relationship tags
    if ($("strong:contains('Relationship')").add("option:selected:contains('Relationship')").length == 0) { return }

    const label = $('<dt><label for="tag_syn_autofill">Autofill Synonym</label></dt>')
    const gf = $('<li><a href="#" id="autofill-gf">Given Family (Western)</a></li>')
    const fg = $('<li><a href="#" id="autofill-fg">Family Given (Eastern)</a></li>')
    const clear = $('<li><a href="#" id="autofill-clear">Clear</a></li>')
    const buttons = $('<ul class="actions" role="menu" style="float: left"></ul>')
    const disambig = $('<ul class="tags commas filters actions" role="menu" style="float: left"><li><label draggable="true" style="cursor: pointer; margin: 0.375em auto; float: left"><span>Disambiguate </span><input type="checkbox" value="1" id="keep_disambig"><span class="indicator" id="kdi" aria-hidden="true"></span></label></li></ul>')
    document.styleSheets[0].addRule('#kdi:before','border: 0; margin-right: 0;')
    const dd = $('<dd></dd>')
    const prev = $("input#tag_syn_string").parent()

    gf.click(x => autofill($, true))
    fg.click(x => autofill($, false))
    clear.click(x => { $("input#tag_syn_string_autocomplete").val(""); $("input#tag_canonical").prop("checked", false) })
    buttons.append(gf, fg, clear)
    dd.append(buttons)
    buttons.after(disambig)
    prev.after(label, dd)

})(jQuery);