LibraryThing custom author roles

If you constantly need to add an "Other..." type of role instead of the ones in the pre-approved list, this script will add those other roles automatically to the dropdown

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

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

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

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

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        LibraryThing custom author roles
// @namespace   https://greasyfork.org/en/users/11592-max-starkenburg
// @description If you constantly need to add an "Other..." type of role instead of the ones in the pre-approved list, this script will add those other roles automatically to the dropdown
// @include     http*://*librarything.tld/work/*
// @include     http*://*librarything.com/work/*
// @include     http*://*librarything.tld/addnew.php*
// @include     http*://*librarything.com/addnew.php*
// @version     5
// @grant       none
// ==/UserScript==

// Make a comma-separated list of "Other" role types you'd like to be in the dropdown menu by default
var customRolesString = "Ghostwriter, Corporate author, Compiler, Interviewee, Penciller, Letterer, Inker, Colorist, Director, Producer, Actor, Performer";
var suppressRolesString = "";

// If you've made edits to the list of custom or suppressed roles, get those instead
if (localStorage.LTcustomRoles) customRolesString = localStorage.LTcustomRoles;
if (localStorage.LTsuppressRoles) suppressRolesString = localStorage.LTsuppressRoles;

// Make any array of the custom roles, and deal with certain special characters, extra spaces/commas, etc.
var customRoles = makeRolesArray(customRolesString);
var suppressRoles = makeRolesArray(suppressRolesString);
function makeRolesArray(rolesString) {
  if (rolesString == null || rolesString.trim() == "") {
    return [];
  } else {
    rolesArray = rolesString
      .replace(/&/g, "&") // escape ampersands (though LT sometimes chokes on these anyway)
      .replace(/\"/g, """) // escape double quotes
      .replace(/</g, "&lt;") // escape less thans (though LT sometimes chokes on these anyway)
      .replace(/>/, "&gt;") // escape greater thans
      .trim() // remove exterior leading/trailing spaces
      .split(/\s*,+\s*/); // split with comma delimiter, plus removing leading/trailing spaces on each piece
    return rolesArray;
  }
}

// When focusing (clicking) or arrowing through (keydown) a dropdown menun, add your custom roles to it.
// Had to put it on "body" because the menus in the "Add/edit other authors" of the work page are dynamically added.
jQuery("body").on("focus keydown", "#bookedit_roles select, #bookedit_authorunflip select, .editotherauthorstable select", function(evt) {

  // Don't bother to run the below code if it's already been done for this <select>
  if (!jQuery(this).hasClass("gm-custom-roles-select")) {
    
    // For certain things we need to know whether we're editing our book, or setting authors on the whole work
    var pageType = jQuery(this).closest("#bookedit_roles, #bookedit_authorunflip").length ? "book" : "work";
    
    // Remember the "selected" element, in order to put the selection property back after the sort
    selectedOpt = jQuery(this).children(":selected");
    selected = { val: selectedOpt.attr("value"), text: selectedOpt.text() };

    // Get the existing roles from the <select><option> texts (not value, since some don't reflect the text)
    var existingRoles = jQuery(this).children("option");
    
    // Remove any empty strings from customRoles and suppressRoles
    for ( var i=0; i<customRoles.length; i++ ) {
      if ( customRoles[i] == '') delete customRoles[i];
      // Remove any custom roles that are already in the list
      for ( var j=0; j<existingRoles.length; j++ ) {
        if (customRoles[i] == existingRoles.eq(j).text()) {
          delete customRoles[i];
        }
      } 
    }
    for ( var k=0; k<suppressRoles.length; k++ ) {
      if (suppressRoles[k] == '') delete suppressRoles[k];
    }

    // Move away any that have been added to the suppression list, unless it was "selected"
    var suppressedSelect = jQuery("#gm-suppressed-select");
    if (!suppressedSelect.length) { // Make a hidden select to store away the values of suppressed roles
      suppressedSelect = jQuery('<select id="gm-suppressed-select" class="gm-suppressed-select-empty"></select>');
      suppressedSelect.appendTo("body").hide();
    }
    for ( var l=0; l<suppressRoles.length; l++ ) {
      for ( var m=0; m<existingRoles.length; m++) {
        if (suppressRoles[l] == existingRoles.eq(m).text()) {
          if (suppressedSelect.not(".gm-suppressed-select-empty")) { // Just fill it once until such time that the suppressed roles change
            existingRoles.eq(m).clone().appendTo(suppressedSelect);
          }
          if (!existingRoles.eq(m).is(":selected")) {
            existingRoles.eq(m).remove();
          }
        }
      }
    }
    suppressedSelect.removeClass("gm-suppressed-select-empty");

    // Add <option>s for adding custom roles or suppressing standard ones
    var invalidValue = pageType == "book" ? "" : "-1"; // The value of invalid options depends on their location
    var lineBeforeOther;
    if (pageType == "book") {
      lineBeforeOther = jQuery(this).children("option[value='xxxOTHERxxx']").prev();
    } else {
      lineBeforeOther = jQuery(this).children("option[value='-1']").eq(-2);
    }
    lineBeforeOther.before('<option value="' + invalidValue + '" class="gm-custom-roles-option gm-custom-roles-line">--------------</option>');
    lineBeforeOther.before('<option value="' + invalidValue + '" class="gm-custom-roles-option gm-edit-custom-roles">Add custom roles...</option>');
    lineBeforeOther.before('<option value="' + invalidValue + '" class="gm-custom-roles-option gm-suppress-standard-roles">Suppress standard roles...</option>');

    // Add <option>s for each of the new custom roles
    for ( var n=0; n<customRoles.length; n++ ) {
      if (customRoles[n] !== undefined) {
        jQuery(this).append('<option value="' + customRoles[n] + '" class="gm-custom-roles-option">' + customRoles[n] + '</option>');
      }
    }
    
    // Sort the custom and standard roles alphabetically
    // Thanks to SolutionYogi at http://stackoverflow.com/a/1134983/752122
    var roleOptions = jQuery(this).children("option:eq(1)").nextUntil(".gm-custom-roles-option"); // The ones that were already in the list before "Other ..."
    roleOptions = roleOptions.add(lineBeforeOther.next().nextAll()); // The ones that have been added after "Other ..."
    roleOptions = roleOptions.get().sort(function(a, b) {
      return jQuery(a).text().localeCompare(jQuery(b).text());
    });
    jQuery(this).children("option:eq(1)").after(roleOptions);

    // Set the selected option again after this list has been rearranged
    var options = jQuery(this).children("option");
    var newSelectedIndex = 0;
    for(var o=0; o<options.length; o++) {
      currentOpt = options.eq(o);
      if (currentOpt.attr("value") == selected.val && currentOpt.text() == selected.text) {
        newSelectedIndex = o;
        break;
      }
    }
    jQuery(this).get(0).selectedIndex = newSelectedIndex;

    // Mark this <select> as processed so that we're not running all the above code on each and every focus/keydown
    jQuery(this).addClass("gm-custom-roles-select");

  }

});

// Edit the roles to whatever you want
jQuery("body").on("change", "#bookedit_roles select, #bookedit_authorunflip select, .editotherauthorstable select", function(evt) {
  var selected = jQuery(this).children(":selected");
//  console.log(jQuery(this).children().last().text());
  // Add new custom roles
  if (selected.hasClass("gm-edit-custom-roles") ) {
    var editRoles = prompt("Change which custom roles you would like to be appear in the dropdown (comma-separated)",customRolesString);
    if (editRoles != null) {
      // Store the user's preferred roles in localStorage (and reset customRoles[String] too)
      localStorage.LTcustomRoles = customRolesString = editRoles;
      customRoles = makeRolesArray(editRoles);
      resetSelects(jQuery(this));
    }
  // Or suppress the existing ones
  } else if (selected.hasClass("gm-suppress-standard-roles") ) {
    var editSuppressRoles = prompt("Enter a comma-separated list of the roles you never use, e.g. Afterword, Narrator, etc.",suppressRolesString);
    if (editSuppressRoles != null) {
      // Store the user's suppressed roles in localStorage (and reset suppressRoles[String] too)
      localStorage.LTsuppressRoles = suppressRolesString = editSuppressRoles;
      suppressRoles = makeRolesArray(editSuppressRoles);
      resetSelects(jQuery(this));
    }
  }
});

// Reset the lists that had been previously processed so that they can get any updates ...
function resetSelects(select) {
  select.blur();
  jQuery(".gm-custom-roles-option")
    .not(":selected") // excluding custom <option>s that were already selected (don't want to undo previous work)
    .add(".gm-custom-roles-line, .gm-edit-custom-roles, .gm-suppress-standard-roles") // unless it was "Add/suppress roles ..." (such a selection doesn't need to be preserved)
    .remove();
  jQuery(".gm-custom-roles-select").append(jQuery("#gm-suppressed-select").children().clone());
  jQuery("#gm-suppressed-select").remove();
  jQuery(".gm-custom-roles-select").removeClass("gm-custom-roles-select");
}