// ==UserScript== // @name AO3: just my languages // @namespace https://greasyfork.org/en/users/757649-certifieddiplodocus // @version 1.2 // @description Reduce language options to your preferences // @author CertifiedDiplodocus // @require http://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js // @match http*://archiveofourown.org/* // @exclude /^https?:\/\/archiveofourown\.org(?!\/search$|(.*\/(works|bookmarks)(?![^\/?])))/ // @exclude /.org/(works|bookmarks)$/ // @exclude /(works|bookmarks)\/search[?](?!.*edit_search=true)/ // @exclude /\/works\/[0-9]+(?![0-9]*\/edit)/ // @exclude /\/bookmarks\/[0-9]+ // @icon data:image/svg+xml,𒈾 // @grant GM_addStyle // @license GPL-3.0-or-later // @downloadURL none // ==/UserScript== /* eslint-env jquery */ // allows jQuery /* PURPOSE: Simplify language search options on AO3. Choose any combination of the following: 1 - Show only your chosen languages in the dropdown list (when filtering and/or creating or editing a work) 2 - Bold some languages in the dropdown (can be chosen independently from option 1) 3 - Autofill: - Monolingual: automatically set the dropdown to your preferred language - Multilingual: add a query to each search to show fic in multiple languages at once (e.g. English AND Spanish AND Thai) 4 - Manual multilingual search: click the 𒈾 button to add/remove multilingual filters. Also works with autofill. When creating or editing a work, you can: 5 - Show only your chosen languages in the dropdown 6 - Set a default language (⚠ use with caution ⚠) A word to the wise: don't overlook languages close to yours (e.g. if you read English you can read Scots, if you read Spanish you have a fair shot at Galego and Asturianu, etc) ---------------------------------------------------------------------------------------------------------------------- */ (function() { 'use strict'; /* ======================== 𒈾 USER SETTINGS (save to plaintext file in case of script updates) 𒈾 ============================== LANGUAGE CODES are listed at the end of this script. (AO3 appears to use a mix of 2 and 3-character codes.) Leave blank for the AO3 default appearance. OPTIONAL: autofill new searches to your chosen language(s). Filters can still be changed by hand. This carries a small risk of hiding mistagged fics, so is disabled by default (autofillSearch = 0). 0 - AO3 default (blank dropdown) 1 - MONOLINGUAL autofill (fills the dropdown with your preferred language) 2 - MULTILINGUAL autofill (adds a search query to show fic in all your preferred languages at once) * note: may be slower / have more impact on the servers. If noticeable, try reducing the number of languages. */ const dropdownLanguages = ['en', 'es', 'fr', 'ptBR', 'ptPT', 'sux'] const boldedLanguages = ['en', 'es', 'fr'] const languagesForMultilingualSearch = ['ptBR', 'ptPT', 'sux'] const modifyFilterDropdown = true const autofillSearch = 0 const defaultLanguage = 'es' // new work / new imported work / edit work const modifyEditorDropdown = true const defaultWritingLanguage = '' // OPTIONAL: add a language to autofill on new works // =============================================================================================================================== // Check that settings make sense const errPrefix = '[AO3: just my languages - userscript] \n⚠ Error: ' if (!dropdownLanguages.some(Boolean) && (modifyFilterDropdown || modifyEditorDropdown)) { throw errPrefix + "To modify the dropdown you must add some languages first!" } if (autofillSearch===1 && !defaultLanguage) {throw errPrefix + "To autofill the dropdown, add a default language first!"} if (autofillSearch===2 && !(languagesForMultilingualSearch || dropdownLanguages)) {throw errPrefix + "To autofill a multilingual search, add some languages first!"} const pageURL = window.location.href let dropdown, searchbox // ------------------------------------------------------------------------------------------------------------------------------- // Show only selected languages when creating/editing works if ((/\/works\/(new.*|([0-9]+\/edit))/gi).test(pageURL)) { dropdown = $('select[id$="language_id"') // handle language selection in new?imported page, including parent work (IDs are different but all end in "language_id") verifyLanguageCodes() if (modifyFilterDropdown) {reduceDropdownLangs(); boldDropdownLangs()} if (pageURL.includes('/works/new')) {autofillBlankDropdown(defaultWritingLanguage)} return; } // ------------------------------------------------------------------------------------------------------------------------------- // select the right elements for the page if (pageURL.includes('/bookmarks')) { dropdown = $('#bookmark_search_language_id') searchbox = $('#bookmark_search_bookmarkable_query') } else { dropdown = $('#work_search_language_id') searchbox = $('#work_search_query') } verifyLanguageCodes() // show only my languages (and the default 'blank' value) in the dropdown if (modifyFilterDropdown) {reduceDropdownLangs(); boldDropdownLangs()} // Set filter for searching multiple languages. (If user didn't fill in multiLanguages, use dropdownLanguages instead.) const languageFilters = 'language_id:' + (languagesForMultilingualSearch || dropdownLanguages).join(' OR language_id:') /* Autofill (if the dropdown/searchbox are blank) 1 - MONOLINGUAL AUTOFILL: set dropdown to the default language. 2 - MULTILINGUAL AUTOFILL: insert search string into "Search within results / Any field": "language_id:egy OR language_id:sux" */ switch (autofillSearch) { case 1: autofillBlankDropdown(defaultLanguage); break; case 2: if ($.trim(searchbox.val()).length == 0) { searchbox.val(languageFilters) } } // Add (𒈾) button for multilingual searches next to "Languages" label. const dropdownLabel = dropdown.parent().prev() const babelButton = $(`𒈾`) dropdownLabel.append(babelButton) // On click of (𒈾), add OR remove language filters from the "all fields" searchbox (after the current query) babelButton.click(function(){ const searchboxContent = $.trim(searchbox.val()) if (searchboxContent.length == 0) { searchbox.val(languageFilters) } else if (!searchboxContent.includes(languageFilters)) { searchbox.val(searchboxContent + ' ' + languageFilters) // toggle on } else { searchbox.val(searchboxContent.replace(languageFilters,'').trim()) // toggle off } searchbox.trigger('change') }) // Conditional CSS: alignment + colour (on search pages, 𒈾 should use the default page style to match the neighbouring "?") babelButton.children().first().toggleClass('babel-normal-align', !window.location.href.includes('/search')) searchbox.on('change', indicateBabelStatus) indicateBabelStatus() // -------- FUNCTIONS ------------------------------------------------------------------------------------------------------------ // Colour 𒈾 green as long as the search box contains the filter (check with different site skins). Set the tooltip. function indicateBabelStatus() { const languageFiltersOn = searchbox.val().includes(languageFilters) babelButton.children().first().toggleClass('babel-button-filter-on', languageFiltersOn) babelButton .attr('title',`${languageFiltersOn ? 'Searching' : 'Search'} multiple languages: ${languagesForMultilingualSearch.join(', ')}`) // in template literals, make a newline to break, no code needed! } // Show only your chosen languages (+ blank option) in the dropdown (filter or editing) function reduceDropdownLangs(){ dropdown.children().hide() dropdown.children('[value=""]').show() for (let userLang of dropdownLanguages) { dropdown.children(`[lang="${userLang}"]`).show(); } } // Bold languages in the dropdown function boldDropdownLangs() { if (!boldedLanguages.some(Boolean)) {return} for (let userLang of boldedLanguages) { dropdown.children(`[lang="${userLang}"]`).css('font-weight','bold'); } } // Autofill the dropdown if it is empty and the user selected a default language function autofillBlankDropdown(defaultLang) { if (!defaultLang || dropdown.val()) {return} dropdown.children(`[lang="${defaultLanguage}"]`).attr('selected','selected') } // Check that all user languages exist (run after the dropdown is set) function verifyLanguageCodes() { if (!dropdown.length) {console.error(errPrefix + 'No language dropdown found!'); return} // prevent false alarms if the dropdown didn't load, but trace errors const allUserLanguages = new Set( // no duplicates [...dropdownLanguages, ...boldedLanguages, ...languagesForMultilingualSearch, defaultLanguage, defaultWritingLanguage] .filter(x => x) // no empty values ) let languageCodesNotFound = [] for (let userLang of allUserLanguages) { if (!dropdown.children(`[lang="${userLang}"]`).length) { languageCodesNotFound.push(userLang) } } if (!languageCodesNotFound.length) {return true} console.error(errPrefix + 'Could not find these language codes: "' + languageCodesNotFound.join(", ") + '"\n' + 'Please check your settings for typos.\n\n' + 'User-selected languages: ' + [...allUserLanguages].join(", ")) return false } //--------- Style 𒈾 button in CSS. ------------------------------------------------------------------------------------------- // SPECIFICITY hierarchy: inline > #id > .class > attribute (e.g '[type="text"]') > element (e.g 'p'). Remember: p.class > .class GM_addStyle ( ` .babel-button { cursor:copy; } span.babel-normal-align { vertical-align:inherit; } span.babel-button-filter-on { color: mintcream; background-color: darkgreen; border-color: darkgreen; } `) /*--------- LANGUAGE CODES ON AO3 ------------------------------------------------------------------------------------------------ so: af Soomaali afr: Afrikaans ain: Aynu itak | アイヌ イタㇰ ar: العربية amh: አማርኛ egy: 𓂋𓏺𓈖 𓆎𓅓𓏏𓊖 arc: ܐܪܡܝܐ | ארמיא hy: հայերեն ase: American Sign Language ast: asturianu id: Bahasa Indonesia ms: Bahasa Malaysia bg: Български bn: বাংলা jv: Basa Jawa ba: Башҡорт теле be: беларуская bos: Bosanski br: Brezhoneg ca: Català ceb: Cebuano cs: Čeština chn: Chinuk Wawa crh: къырымтатар тили | qırımtatar tili cy: Cymraeg da: Dansk de: Deutsch et: eesti keel el: Ελληνικά sux: 𒅴𒂠 en: English ang: Eald Englisċ es: Español eo: Esperanto eu: Euskara fa: فارسی fil: Filipino fr: Français frr: Friisk fur: Furlan ga: Gaeilge gd: Gàidhlig gl: Galego got: 𐌲𐌿𐍄𐌹𐍃𐌺𐌰 gyn: Creolese hak: 中文-客家话 ko: 한국어 hau: Hausa | هَرْشَن هَوْسَ hi: हिन्दी hr: Hrvatski haw: ʻŌlelo Hawaiʻi ia: Interlingua zu: isiZulu is: Íslenska it: Italiano he: עברית kal: Kalaallisut kan: ಕನ್ನಡ kat: ქართული cor: Kernewek khm: ភាសាខ្មែរ qkz: Khuzdul sw: Kiswahili ht: kreyòl ayisyen ku: Kurdî | کوردی kir: Кыргызча fcs: Langue des signes québécoise lv: Latviešu valoda lb: Lëtzebuergesch lt: Lietuvių kalba la: Lingua latina hu: Magyar mk: македонски ml: മലയാളം mt: Malti mnc: ᠮᠠᠨᠵᡠ ᡤᡳᠰᡠᠨ qmd: Mando'a mr: मराठी mik: Mikisúkî mon: ᠮᠣᠩᠭᠣᠯ ᠪᠢᠴᠢᠭ᠌ | Монгол Кирилл үсэг my: မြန်မာဘာသာ myv: Эрзянь кель nah: Nāhuatl nan: 中文-闽南话 臺語 ppl: Nawat nl: Nederlands ja: 日本語 no: Norsk azj: Азәрбајҹан дили | آذربایجان دیلی ce: Нохчийн мотт ood: ‘O’odham Ñiok ota: لسان عثمانى ps: پښتو nds: Plattdüütsch pl: Polski ptBR: Português brasileiro ptPT: Português europeu pa: ਪੰਜਾਬੀ kaz: qazaqşa | қазақша qlq: Uncategorized Constructed Languages qya: Quenya ro: Română ru: Русский sco: Scots sq: Shqip sjn: Sindarin si: සිංහල sk: Slovenčina slv: Slovenščina gem: Sprēkō Þiudiskō sr: Српски fi: suomi sv: Svenska ta: தமிழ் tat: татар теле mri: te reo Māori tel: తెలుగు th: ไทย tqx: Thermian bod: བོད་སྐད་ vi: Tiếng Việt cop: ϯⲙⲉⲧⲣⲉⲙⲛ̀ⲭⲏⲙⲓ tlh: tlhIngan-Hol tok: toki pona trf: Trinidadian Creole tsd: τσακώνικα chr: ᏣᎳᎩ ᎦᏬᏂᎯᏍᏗ tr: Türkçe uk: Українська urd: اُردُو uig: ئۇيغۇر تىلى vol: Volapük wuu: 中文-吴语 yi: יידיש yua: maayaʼ tʼàan yue: 中文-广东话 粵語 zh: 中文-普通话 國語 Userscript should activate only on URLs with language filtering. If it doesn't activate on a page, and it should, let me know. (Quick & dirty fix: delete the @exclude lines at the start of the script to enable the script on all of AO3.) Include: all URLS with /works or /bookmarks; new/edit work; the exact url "https://archiveofourown.org/search" Exclude: individual works/bookmarks, advanced search results (no tag filtering = no filter sidebar) INCLUDE EXAMPLES users/singlecrow/pseuds/raven/works?fandom_id=47992099 collections/SomeCollection/bookmarks collections/SomeCollection/works?work_search%5... languages/uig/works bookmarks?bookmark_search%5Bsort_column%5D=cre... bookmarks/search works?work_search works/new works/new?import=true works/123456/edit works/123456/edit_tags (works|bookmarks)/search?.*edit_search=true search?commit=Search&edit_search=true EXCLUDE EXAMPLES works/search?commit=Search&work_search%5Bquery... (no filter bar) works/search?work_search%5Bquery%5D=&work_search... works/search.*(?!edit_search=true) works/123456 bookmarks/123456/edit collections/Suggested_Good_Reads/works/18356108 .org/works .org/bookmarks TAMPERMONKEY REGEX ISSUE: the line @exclude /\/works\/[0-9]+(?![0-9]*\/edit)/ should also block /works/123/comments/edit, but doesn't. See https://stackoverflow.com/questions/68826178/exclude-in-userscript-not-working-as-expected for another script where the regex fails in Tampermonkey, but not Violentmonkey */ })(jQuery);