AO3: [Wrangling] Synonym Autofill

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

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 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);