您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
makes the Tag Search form take up less space (best on desktop/landscape screens)
// ==UserScript== // @name AO3: [Wrangling] Smaller Tag Search // @namespace https://greasyfork.runtimutd.eu.org/en/users/906106-escctrl // @version 6.1 // @description makes the Tag Search form take up less space (best on desktop/landscape screens) // @author escctrl // @match *://*.archiveofourown.org/tags/search* // @require https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js // @require https://ajax.googleapis.com/ajax/libs/jqueryui/1.13.2/jquery-ui.min.js // @require https://update.greasyfork.ip-ddns.com/scripts/491888/1355841/Light%20or%20Dark.js // @grant none // @license MIT // ==/UserScript== /* eslint-disable no-multi-spaces */ /* global jQuery, lightOrDark */ (function($) { 'use strict'; // --- THE USUAL INIT STUFF AT THE BEGINNING ------------------------------------------------------------------------------- // on retry later, break off the rest of this script to avoid console errors if ($('#new_tag_search').length == 0) return; if(document.head.querySelector('link[href$="/font-awesome.min.css"]') === null) $("head").append(`<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">`); let cfg = 'smallertagsearch'; // name of dialog and localstorage used throughout let dlg = '#'+cfg; /* *** EXAMPLE STORAGE: equivalent of a Map() with settings for reducing size and default selections on pageload settings = [ ["text","1"],["labels", "n"],["tag","b"],["sort","b"],["btntag", "t"],["btnsort", "i"], ["deftype","Freeform"],["defstatus",""],["defsortby","name"],["defsortdir","asc"] ]; text: (1) next to each other, (2) below each other with labels tag & sort labels (above options): (y) yes, (n) no tag show as: (b) buttons, (s) select tag button has: (i) icon, (t) text, (b) both sort show as: (b) buttons, (s) select, (h) hide sort button has: (i) icon, (t) text, (b) both */ let settings = loadConfig(); // config migration if (settings.get('defstatus') === 'T') settings.set('defstatus', 'canonical'); else if (settings.get('defstatus') === 'F') settings.set('defstatus', 'noncanonical'); if (settings.get('btntxt') !== undefined) { if (settings.get('btntxt') === 'y') { settings.set('btntag', 'b'); settings.set('btnsort', 'b'); } else { settings.set('btntag', 'i'); settings.set('btnsort', 'i'); } } // display text/icon for search parameters var type_alias = new Map([['Fandom', ['Fandom', `<i class="fa fa-archive"></i>`]], ['Character', ['Character', `<i class="fa fa-user"></i>`]], ['Relationship', ['Relationship', `<i class="fa fa-users"></i>`]], ['Freeform', ['Freeform', `<i class="fa fa-tags"></i>`]], ['', ['Any Type', `<i class="fa fa-asterisk"></i>`]]]); var stat_alias = new Map([['canonical', ['Canonical', `<i class="fa fa-check-square"></i>`, 'Canonicals']], ['noncanonical', ['Not canonical', `<i class="fa fa-square-o"></i>`, 'Any except Canonicals']], ['synonymous', ['Syns', `<i class="fa fa-code-fork"></i>`, 'Synonyms']], ['canonical_synonymous', ['In Filters', `<i class="fa fa-filter"></i>`, 'Canonicals or Synonyms (appearing in filters)']], ['noncanonical_nonsynonymous', ['Unfiltered', `<i class="fa fa-low-vision"></i>`, 'Any except Canonicals or Synonyms (not appearing in filters)']], ['', ['Any Status', `<i class="fa fa-asterisk"></i>`, 'Any wrangling status']]]); var sort_alias = new Map([["name", ["Name", `<i class="fa fa-font"></i>`]], ["created_at", ["Creation Date", `<i class="fa fa-calendar"></i>`]], ["uses", ["Uses", `<i class="fa fa-bar-chart"></i>`]]]); // ASC/DESC translation for the different sort options: x -> [ASC, icon for ASC, DESC, icon for DESC] var dir_alias = new Map([["name", ["A → Z", `<i class="fa fa-sort-alpha-asc"></i>`, "Z → A", `<i class="fa fa-sort-alpha-desc"></i>`]], ["created_at", ["oldest → newest", `<i class="fa fa-sort-amount-asc"></i>`, "newest → oldest", `<i class="fa fa-sort-amount-desc"></i>`]], ["uses", ["fewest → most", `<i class="fa fa-sort-numeric-asc"></i>`, "most → fewest", `<i class="fa fa-sort-numeric-desc"></i>`]]]); let getAscDescAlias = (by, dir) => dir_alias.get(by)[ (dir == "asc" ? 0 : 2) ]; // function to retrieve readable name based on which sort-by is selected let getAscDescIcon = (by, dir) => dir_alias.get(by)[ (dir == "asc" ? 1 : 3) ]; // function to retrieve matching icon based on which sort-by is selected // figure out which option currently needs to be selected, based on search parameters in the URL vs. configured defaults var opt_selected = new Map(); let params = new URLSearchParams(document.location.search); ["tag_search[type]", "tag_search[wrangling_status]", "tag_search[sort_column]", "tag_search[sort_direction]"].forEach((name) => { // if we've already done a search, select that option again if (params.size !== 0) { if (params.get(name)) opt_selected.set(name, params.get(name)); // if this parameter was actually part of the URL else { // otherwise go with what AO3 selects as defaults on partial search strings switch (name) { case "tag_search[type]": case "tag_search[wrangling_status]": opt_selected.set(name, ""); break; case "tag_search[sort_column]": opt_selected.set(name, "name"); break; case "tag_search[sort_direction]": opt_selected.set(name, "asc"); break; default: break; } } } // otherwise pick the configured defaults else { switch (name) { case "tag_search[type]": opt_selected.set(name, settings.get("deftype")); break; case "tag_search[wrangling_status]": opt_selected.set(name, settings.get("defstatus")); break; case "tag_search[sort_column]": opt_selected.set(name, settings.get("defsortby")); break; case "tag_search[sort_direction]": opt_selected.set(name, settings.get("defsortdir")); break; default: break; } } }); // --- CONFIGURATION DIALOG HANDLING ------------------------------------------------------------------------------- createDialog(); function createDialog() { // adding the jQuery stylesheet to style the dialog, and fixing the interference of AO3's styling if(document.head.querySelector('link[href$="/jquery-ui.css"]') === null) { // if the background is dark, use the dark UI theme to match let dialogtheme = lightOrDark($('body').css('background-color')) == "dark" ? "dark-hive" : "base"; $("head").append(`<link rel="stylesheet" href="https://code.jquery.com/ui/1.13.2/themes/${dialogtheme}/jquery-ui.css">`); } $("head").append(`<style type="text/css">.ui-widget, ${dlg}, .ui-dialog .ui-dialog-buttonpane button {font-size: revert; line-height: 1.286;} ${dlg} form {box-shadow: revert; cursor:auto;} ${dlg} fieldset {background: revert; box-shadow: revert; margin-left: 0; margin-right: 0;} ${dlg} legend {font-size: inherit; height: auto; width: auto; opacity: inherit;} ${dlg} fieldset p { padding-left: 0; padding-right: 0; } ${dlg} select { width: auto; } ${dlg} #tagsearchdefaults label { width: 9em; display: inline-block; } </style>`); // wrapper div for the dialog $("#main").append(`<div id="${cfg}"></div>`); let selected = 'selected="selected"'; let checked = 'checked="checked"'; let deftype = "", defstatus = "", defsort = "", wstatshow = ""; let hidewstat = settings.get("hidewstat")?.split(",") || []; // wrangling status options which user chose to hide type_alias.forEach( (v, k) => { deftype += `<option value="${k}" ${k === settings.get("deftype") ? selected : ""}>${v[0]}</option>`; } ); stat_alias.forEach( (v, k) => { defstatus += `<option value="${k}" ${k === settings.get("defstatus") ? selected : ""}>${v[0]}</option>`; if (k !== "") wstatshow += `<label for="tagsearchdisplay_wstat_${k}" title="${v[2]}">${v[1]} ${v[0]}</label><input type="checkbox" name="tagsearchdisplay_wstat_${k}" id="tagsearchdisplay_wstat_${k}" ${hidewstat.includes(k) ? "" : checked}>`; } ); sort_alias.forEach( (v, k) => { defsort += `<option value="${k}-asc" ${k === settings.get("defsortby") && settings.get("defsortdir") === "asc" ? selected : ""}>${v[0]}, ${getAscDescAlias(k, 'asc')}</option> <option value="${k}-desc" ${k === settings.get("defsortby") && settings.get("defsortdir") === "desc" ? selected : ""}>${v[0]}, ${getAscDescAlias(k, 'desc')}</option>`; } ); $(dlg).html(`<form> <fieldset><legend>Display</legend> <p><label for="tagsearchdisplay_text">Show Search Text and Fandom input fields</label><br /> <select name="tagsearchdisplay_text" id="tagsearchdisplay_text" style="width: 20em;"> <option value="1" ${settings.get('text')==="1" ? selected : ""}>next to each other, without labels</option> <option value="2" ${settings.get('text')==="2" ? selected : ""}>below each other, with labels</option> </select> </p> <p> <label for="tagsearchdisplay_labels">Show labels above all button/dropdown options</label> <input type="checkbox" name="tagsearchdisplay_labels" id="tagsearchdisplay_labels" ${settings.get('labels') === "y" ? checked : ""}> </p> </fieldset> <fieldset><legend>Tag Type and Status</legend> <p class="radiocontrol">Show the Tag Type and Status options as<br /> <label for="tag_buttons"><i class="fa fa-toggle-on" aria-hidden="true"></i> Buttons</label><input type="radio" name="tagsearchdisplay_tag" id="tag_buttons" value="b" ${settings.get('tag') === "b" ? checked : ""}> <label for="tag_select"><i class="fa fa-caret-square-o-down" aria-hidden="true"></i> Dropdown</label><input type="radio" name="tagsearchdisplay_tag" id="tag_select" value="s" ${settings.get('tag') === "s" ? checked : ""}> </p> <p id="linked_tag_buttons" ${settings.get('tag') === "b" ? "" : 'style="display: none;"'}> <label for="tagsearchdisplay_btntag">Show the buttons with</label> <select name="tagsearchdisplay_btntag" id="tagsearchdisplay_btntag" style="width: 10em;"> <option value="t" ${settings.get('btntag')==="t" ? selected : ""}>text</option> <option value="i" ${settings.get('btntag')==="i" ? selected : ""}>icon</option> <option value="b" ${settings.get('btntag')==="b" ? selected : ""}>icon + text</option> </select> </p> <p id="tagsearchwranglingstatus">Choose which Tag Status options to display. "Any" will always be displayed.<br /> ${ wstatshow } </p> </fieldset> <fieldset><legend>Sort By and Direction</legend> <p class="radiocontrol">Show the Sort By and Direction options as<br /> <label for="sort_buttons"><i class="fa fa-toggle-on" aria-hidden="true"></i> Buttons</label><input type="radio" name="tagsearchdisplay_sort" id="sort_buttons" value="b" ${settings.get('sort') === "b" ? checked : ""}> <label for="sort_select"><i class="fa fa-caret-square-o-down" aria-hidden="true"></i> Dropdown</label><input type="radio" name="tagsearchdisplay_sort" id="sort_select" value="s" ${settings.get('sort') === "s" ? checked : ""}> <label for="sort_hide"><i class="fa fa-eye-slash" aria-hidden="true"></i> Hidden</label><input type="radio" name="tagsearchdisplay_sort" id="sort_hide" value="h" ${settings.get('sort') === "h" ? checked : ""}> </p> <p id="linked_sort_buttons" ${settings.get('sort') === "b" ? "" : 'style="display: none;"'}> <label for="tagsearchdisplay_btnsort">Show the buttons with</label> <select name="tagsearchdisplay_btnsort" id="tagsearchdisplay_btnsort" style="width: 10em;"> <option value="t" ${settings.get('btnsort')==="t" ? selected : ""}>text</option> <option value="i" ${settings.get('btnsort')==="i" ? selected : ""}>icon</option> <option value="b" ${settings.get('btnsort')==="b" ? selected : ""}>icon + text</option> </select> </p> </fieldset> <fieldset id='tagsearchdefaults'> <legend>Defaults</legend> <p>Pick defaults for the tag type, wrangling status, and sort order.</p> <label for="deftype">Tag Type</label> <select name="tagsearchdefault_type" id="deftype"> ${ deftype } </select><br /> <label for="defstatus">Wrangling Status</label> <select name="tagsearchdefault_status" id="defstatus" style="width: 12em;"> ${ defstatus } </select><br /> <label for="defsort">Sort By</label> <select name="tagsearchdefault_sort" id="defsort" style="width: 20em;"> ${ defsort } </select> </fieldset> <!--<fieldset id='tagsearchquick'> <legend>Quick Search Buttons</legend> </fieldset>--> </form>`); // optimizing the size of the GUI in case it's a mobile device let dialogwidth = parseInt($("body").css("width")); // parseInt ignores letters (px) dialogwidth = dialogwidth > 600 ? 600 : dialogwidth * 0.9; // initialize the dialog (but don't open it) $( dlg ).dialog({ appendTo: "#main", modal: true, title: 'Smaller Tag Search Config', draggable: true, resizable: false, autoOpen: false, width: dialogwidth, position: {my:"center", at: "center top"}, buttons: { Reset: deleteConfig, Save: storeConfig, Cancel: function() { $( dlg ).dialog( "close" ); } } }); // event triggers if form is submitted with the <enter> key $( dlg+" form" ).on("submit", (e) => { e.preventDefault(); storeConfig(); }); // if no other script has created it yet, write out a "Userscripts" option to the main navigation if ($('#scriptconfig').length == 0) { $('#header').find('nav[aria-label="Site"] 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_${cfg}">Smaller Tag Search</a></li>`); // on click, open the configuration dialog $("#opencfg_"+cfg).on("click", function(e) { $( dlg ).dialog('open'); // turn checkboxes and radiobuttons into pretty buttons. only once the dialog is open bc sizing doesn't work correctly on hidden elements $( `${dlg} input[type='checkbox']` ).checkboxradio(); $( `${dlg} select` ).selectmenu({ width: null }); $( `${dlg} input[type='radio'], ${dlg} #tagsearchwranglingstatus input[type='checkbox']` ).checkboxradio({ icon: false }); $( `${dlg} .radiocontrol` ).controlgroup(); // event handlers for reactive GUI: hide icon/text button option if not showing as buttons $( `${dlg} input[name=tagsearchdisplay_tag]` ).on('change', (e) => { if (e.target.value == "b") $(`${dlg} #linked_tag_buttons`).show(); else $(`${dlg} #linked_tag_buttons`).hide(); }); $( `${dlg} input[name=tagsearchdisplay_sort]` ).on('change', (e) => { if (e.target.value == "b") $(`${dlg} #linked_sort_buttons`).show(); else $(`${dlg} #linked_sort_buttons`).hide(); }); }); } // --- LOCALSTORAGE MANIPULATION ------------------------------------------------------------------------------- function loadConfig() { // load storage on page startup, or default values if there's no storage item return new Map(JSON.parse(localStorage.getItem(cfg) || `[["text","1"],["labels","n"],["tag","b"],["sort","s"],["btntag","i"],["btnsort","i"],["deftype",""],["defstatus",""],["defsortby","name"],["defsortdir","asc"]]`)); } function deleteConfig() { // deselects all buttons, empties all fields in the form $(dlg+' form').trigger("reset"); // deletes the localStorage localStorage.removeItem(cfg); $( dlg ).dialog( "close" ); location.reload(); } function storeConfig() { // fill a Map() with the choices in the Config GUI let toStore = new Map(); let hidewstat = []; // checkboxes: show labels? $(`${dlg} input[type='checkbox']`).each( function() { if ($(this).prop('name') == "tagsearchdisplay_labels") { if ($(this).prop('checked')) toStore.set('labels', "y"); else toStore.set('labels', "n"); } else if ($(this).prop('name').startsWith("tagsearchdisplay_wstat_")) { let wstat = $(this).prop('name').slice(23); if (!$(this).prop('checked')) hidewstat.push(wstat); } } ); // radiobuttions: how to show tag type/status and sort by/direction $(`${dlg} input[type='radio']:checked`).each( function() { if ($(this).prop('name') == "tagsearchdisplay_tag") toStore.set('tag', $(this).prop('value')); else if ($(this).prop('name') == "tagsearchdisplay_sort") toStore.set('sort', $(this).prop('value')); } ); // selects: how many lines for textinput fields, what to select by default $(`${dlg} select`).each( function() { let name = $(this).prop('name'), value = $(this).prop('value'); if (name == "tagsearchdisplay_text") toStore.set('text', value); else if (name == "tagsearchdisplay_btntag") toStore.set('btntag', value); else if (name == "tagsearchdisplay_btnsort") toStore.set('btnsort', value); else if (name == "tagsearchdefault_type") toStore.set('deftype', value); else if (name == "tagsearchdefault_status") toStore.set('defstatus', value); else if (name == "tagsearchdefault_sort") { toStore.set('defsortby', value.slice(0, value.indexOf("-"))); toStore.set('defsortdir', value.slice(value.indexOf("-")+1)); } } ); // sets the localStorage (turn Map() into an Array for stringify to understand it) // btw this overwrites any old configurations, since we're still using the same key name toStore.set('hidewstat', hidewstat.join(',')); localStorage.setItem(cfg, JSON.stringify(toStore.entries().toArray())); $( dlg ).dialog( "close" ); location.reload(); } // --- WRITING THE NEW TAG SEARCH ------------------------------------------------------------------------------- // for the fields to move/wrap nicely no matter the screen width, we have to group: the two text fields vs. the four selectors (including their respective labels) $('#new_tag_search dl > *').slice(0,4).wrapAll('<div id="smallsearch_first"></div>'); $('#new_tag_search dl > *').slice(1).wrapAll('<div id="smallsearch_second"></div>'); // general CSS for the fields and flexbox for the four selects let custom_css = ` #fandom-field-description { display: none; } #new_tag_search #smallsearch_second { display: flex; flex-flow: row wrap; column-gap: 1rem; row-gap: 0rem; } #new_tag_search #smallsearch_second dd { width: auto; } #new_tag_search dd li.input { margin: 0; } #new_tag_search input[type="text"]::placeholder { opacity: 0.5; font-style: italic; } `; // one-line display: flexbox to move underneath each other on small screens, hide the appropriate <label>s and their <dt>s if (settings.get('text') == "1") { $('#new_tag_search dt').hide(); custom_css += ` #new_tag_search #smallsearch_first { display: flex; flex-flow: row wrap; column-gap: 2%; row-gap: 0rem; align-items: flex-end; } #new_tag_search #smallsearch_first dd { width: 49%; flex-grow: 1; min-width: 15em; } `; // adding a placeholder text to the <input> fields since the labels are gone $('input#tag_search_name').prop('placeholder', 'Tag Name'); $('input#tag_search_fandoms_autocomplete').prop('placeholder', 'Fandom'); } // two-line display with labels in separate flexboxes, or the second label would always move up into the first row else { $('#new_tag_search #smallsearch_second dt').hide(); $('#new_tag_search #smallsearch_first > *').slice(0,2).wrapAll('<div id="smallsearch_firstA"></div>'); $('#new_tag_search #smallsearch_first > *').slice(1).wrapAll('<div id="smallsearch_firstB"></div>'); custom_css += ` #new_tag_search #smallsearch_firstA, #new_tag_search #smallsearch_firstB { display: flex; flex-flow: row wrap; column-gap: 1rem; row-gap: 0rem; align-items: flex-end; } #new_tag_search #smallsearch_first dt { float: none; align-self: start; max-width: 10em; } #new_tag_search #smallsearch_first dd { min-width: 15em; width: unset; flex-grow: 1; } `; } $("head").append("<style type='text/css'>" + custom_css + "</style>"); let labels = settings.get('labels') == "y" ? true : false; let btntag = settings.get('btntag'); let btnsort = settings.get('btnsort'); // (code readability) calling functions that'll rewrite the choices into buttons or selects, per config writeTagTypeChoices(); writeTagStatusChoices(); if (settings.get('sort') === "h") { // hide the sort by/dir from view (but their original <select> are still there) $('#new_tag_search #smallsearch_second dd').slice(2).hide(); // select the correct <option> in the background so default sort config will still work $('#new_tag_search select[name="tag_search[sort_column]"]').find(`option[value="${opt_selected.get("tag_search[sort_column]")}"]`).prop('selected', true); $('#new_tag_search select[name="tag_search[sort_direction]"]').find(`option[value="${opt_selected.get("tag_search[sort_direction]")}"]`).prop('selected', true); } else { // if not hidden, build them as buttons or selects writeSortByChoices(); writeSortDirChoices(); } // put cursor in the search term field if (params.size === 0) $('#tag_search_name').focus(); // --- HELPER FUNCTIONS TO WRITE PAGE HTML ------------------------------------------------------------------------------- function writeTagTypeChoices() { let choices = $('#new_tag_search input[name="tag_search[type]"]'); let style = settings.get('tag') || "s"; let html = `<div id="search_type_choice">`; if (labels) html += `<label for="tag_search[type]">Tag Type</label><br />`; if (style === "b") { // buttons in control group $(choices).each(function() { let alias = type_alias.get(this.value); html += `<label for="${this.id}" title="${alias[0]}">${btntag !== "t" ? alias[1] : ""} ${btntag !== "i" ? alias[0] : "" }</label> <input type="radio" id="${this.id}" name="tag_search[type]" value="${this.value}" ${ opt_selected.get("tag_search[type]") === this.value ? 'checked="checked"' : "" }>`; }); html += "</div>"; $('#new_tag_search #smallsearch_second dd:nth-of-type(1)').html(html); $('input[name="tag_search[type]"]').checkboxradio({ icon: false }); $('#search_type_choice').controlgroup(); } else if (style === "s") { // dropdown select html += `<select name="tag_search[type]" style="width: 10em">`; $(choices).each(function() { let alias = type_alias.get(this.value); html += `<option value="${this.value}">${alias[0]}</option>`; }); html += "</select></div>"; $('#new_tag_search #smallsearch_second dd:nth-of-type(1)').html(html); // write the new <select> to page $('#new_tag_search select[name="tag_search[type]"]').find(`option[value="${opt_selected.get("tag_search[type]")}"]`).prop('selected', true); // select the correct <option> if (settings.get('sort') === 'b') $('#new_tag_search select[name="tag_search[type]"]').selectmenu({ width: null }); // prettify } } function writeTagStatusChoices() { let choices = $('#new_tag_search input[name="tag_search[wrangling_status]"]'); let style = settings.get('tag') || "s"; let hidden = settings.get("hidewstat")?.split(",") || []; // wrangling status options which user chose to hide let html = `<div id="search_status_choice">`; if (labels) html += `<label for="tag_search[wrangling_status]">Tag Status</label><br />`; if (style === "b") { // buttons in control group $(choices).each(function() { if (!hidden.includes(this.value) || this.value === "" || opt_selected.get("tag_search[wrangling_status]") === this.value) { let alias = stat_alias.get(this.value); html += `<label for="${this.id}" title="${alias[2]}">${btntag !== "t" ? alias[1] : ""} ${btntag !== "i" ? alias[0] : "" }</label> <input type="radio" id="${this.id}" name="tag_search[wrangling_status]" value="${this.value}" ${ opt_selected.get("tag_search[wrangling_status]") === this.value ? 'checked="checked"' : "" }>`; } }); html += "</div>"; $('#new_tag_search #smallsearch_second dd:nth-of-type(2)').html(html); $('input[name="tag_search[wrangling_status]"]').checkboxradio({ icon: false }); $('#search_status_choice').controlgroup(); } else if (style === "s") { // dropdown select html += `<select name="tag_search[wrangling_status]" style="width: 10em">`; $(choices).each(function() { if (!hidden.includes(this.value) || this.value === "" || opt_selected.get("tag_search[wrangling_status]") === this.value) { let alias = stat_alias.get(this.value); html += `<option value="${this.value}">${alias[0]}</option>"`; } }); html += "</select></div>"; $('#new_tag_search #smallsearch_second dd:nth-of-type(2)').html(html); // write the new <select> to page $('#new_tag_search select[name="tag_search[wrangling_status]"]').find(`option[value="${opt_selected.get("tag_search[wrangling_status]")}"]`).prop('selected', true); // select the correct <option> if (settings.get('sort') === 'b') $('#new_tag_search select[name="tag_search[wrangling_status]"]').selectmenu({ width: null }); // prettify } } function writeSortByChoices() { let choices = $('#new_tag_search select[name="tag_search[sort_column]"] option'); let style = settings.get('sort') || "s"; let html = `<div id="search_sort_choice">`; if (style === "b") { // buttons in control group if (labels) html += `<label for="tag_search[sort_column]">Sort By</label><br />`; $(choices).each(function() { let alias = sort_alias.get(this.value); html += `<label for="tag_search_sort_${this.value}" title="${alias[0]}">${btnsort !== "t" ? alias[1] : ""} ${btnsort !== "i" ? alias[0] : "" }</label> <input type="radio" id="tag_search_sort_${this.value}" name="tag_search[sort_column]" value="${this.value}" ${ opt_selected.get("tag_search[sort_column]") === this.value ? 'checked="checked"' : "" }>`; }); html += "</div>"; $('#new_tag_search #smallsearch_second dd:nth-of-type(3)').html(html); // jQueryUI make it pretty $('input[name="tag_search[sort_column]"]').checkboxradio({ icon: false }); $('#search_sort_choice').controlgroup(); // change eventhandler (on any <input> = button within this controlgroup) to dynamically update the ASC/DESC labels $('#search_sort_choice').on('change', "input", function() { onSortByChange('BUTTON'); }); } else if (style === "s") { // dropdown select let select = $('#new_tag_search select[name="tag_search[sort_column]"]').css('width', '15em'); if (!labels) $(choices).prepend("Sort by "); // add the "sort by" text into the <option>s if the labels are hidden else $(select).before(`<label for="tag_search[sort_column]">Sort By</label><br />`); $(choices).each(function() { if (opt_selected.get("tag_search[sort_column]") === this.value) $(this).prop('selected', true); }); // jQueryUI make it pretty (width null forces original size) - with a change eventhandler to dynamically update the ASC/DESC labels if (settings.get('tag') === 'b') $( select ).selectmenu({ width: null, change: function(event, ui) { onSortByChange('SELECT'); } }); else $( select ).on('change', function() { onSortByChange('SELECT'); }); } } function writeSortDirChoices() { let choices = $('#new_tag_search select[name="tag_search[sort_direction]"] option'); let style = settings.get('sort') || "s"; let html = `<div id="search_order_choice">`; if (style === "b") { // buttons in control group if (labels) html += `<label for="tag_search[sort_direction]">Sort Direction</label><br />`; $(choices).each(function() { let dir_readable = getAscDescAlias($('[name="tag_search[sort_column]"]:checked').prop('value'), this.value); // readable name depends on which sort-by is selected let dir_icon = btnsort === "i" ? getAscDescIcon($('[name="tag_search[sort_column]"]:checked').prop('value'), this.value) : `<i class="fa fa-sort-amount-${this.value}"></i>`; html += `<label for="tag_search_sort_${this.value}" title="${dir_readable}">${btnsort !== "t" ? dir_icon : ""} ${btnsort !== "i" ? dir_readable : ""}</label> <input type="radio" id="tag_search_sort_${this.value}" name="tag_search[sort_direction]" value="${this.value}" ${ opt_selected.get("tag_search[sort_direction]") === this.value ? 'checked="checked"' : "" }>`; }); html += "</div>"; $('#new_tag_search #smallsearch_second dd:nth-of-type(4)').html(html); // jQueryUI make it pretty $('input[name="tag_search[sort_direction]"]').checkboxradio({ icon: false }); $('#search_order_choice').controlgroup(); } else if (style === "s") { // dropdown select let select = $('#new_tag_search select[name="tag_search[sort_direction]"]').css('width', '13em'); if (labels) $(select).before(`<label for="tag_search[sort_direction]">Sort Direction</label><br />`); // change ASC/DESC into something human-readable $(select).find('option').each(function() { let dir_readable = getAscDescAlias($('[name="tag_search[sort_column]"]').prop('value'), this.value); this.innerText = dir_readable; if (opt_selected.get("tag_search[sort_direction]") === this.value) $(this).prop('selected', true); }); // jQueryUI make it pretty (width null forces original size) if (settings.get('tag') === 'b') $( select ).selectmenu({ width: null }); } } // --- DELEGATED EVENT HANDLERS FOR REACTIVE PAGE ------------------------------------------------------------------------------- // event handler listening to user changing the sort by field so we can update the text on the ASC/DESC function onSortByChange(elemType) { if (elemType === "SELECT") { // grab the now selected sort-by let new_sort_by = $('[name="tag_search[sort_column]"]').prop('value'); // update the labels of the ASC/DESC on our original form elements $('#new_tag_search [name="tag_search[sort_direction]"] option').each(function() { let dir_readable = getAscDescAlias(new_sort_by, this.value); this.innerText = dir_readable; }); // refresh the jQueryUI elements to show the same new labels $('#new_tag_search select[name="tag_search[sort_direction]"]').selectmenu( "refresh" ); } else { // grab the now selected sort-by let new_sort_by = $('[name="tag_search[sort_column]"]:checked').prop('value'); // update the labels of the ASC/DESC on our original form elements let dir_readable = getAscDescAlias(new_sort_by, 'asc'); let dir_icon = btnsort === "i" ? getAscDescIcon(new_sort_by, 'asc') : `<i class="fa fa-sort-amount-asc"></i>`; $('#new_tag_search label[for="tag_search_sort_asc"]').prop('title', dir_readable).html( `${btnsort !== "t" ? dir_icon : ""} ${btnsort !== "i" ? dir_readable : ""}` ); dir_readable = getAscDescAlias(new_sort_by, 'desc'); dir_icon = btnsort === "i" ? getAscDescIcon(new_sort_by, 'desc') : `<i class="fa fa-sort-amount-desc"></i>`; $('#new_tag_search label[for="tag_search_sort_desc"]').prop('title', dir_readable).html( `${btnsort !== "t" ? dir_icon : ""} ${btnsort !== "i" ? dir_readable : ""}` ); } } })(jQuery);