// ==UserScript== // @name SiteTagSearches // @namespace https://github.com/BrokenEagle/JavaScripts // @version 5.5 // @description Presents additional site links for the wiki tag(s). // @source https://danbooru.donmai.us/users/23799 // @author BrokenEagle // @match https://*.donmai.us/* // @exclude /^(?!https:\/\/\w+\.donmai\.us\/(posts|wiki_pages\/[^\/?]+|settings)?\/?(\?|$)).*/ // @exclude /^https:\/\/\w+\.donmai\.us\/[^.]*\.(xml|json|atom)(\?|$)/ // @grant none // @run-at document-end // @downloadURL https://raw.githubusercontent.com/BrokenEagle/JavaScripts/master/SiteTagSearches.user.js // @updateURL https://raw.githubusercontent.com/BrokenEagle/JavaScripts/master/SiteTagSearches.user.js // @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20251218/lib/module.js // @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20251218/lib/debug.js // @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20251218/lib/utility.js // @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20251218/lib/storage.js // @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20251218/lib/validate.js // @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20251218/lib/load.js // @require https://raw.githubusercontent.com/BrokenEagle/JavaScripts/20251218/lib/menu.js // ==/UserScript== /* global $ JSPLib */ /****Library updates****/ ////NONE /****Global variables****/ //Exterior script variables const DANBOORU_TOPIC_ID = '14958'; //Variables for load.js const PROGRAM_LOAD_REQUIRED_VARIABLES = ['window.jQuery']; const PROGRAM_LOAD_OPTIONAL_SELECTORS = ['#c-wiki-pages #a-show', '#c-posts #a-index', '#c-users #a-edit']; //Program name constants const PROGRAM_SHORTCUT = 'sts'; const PROGRAM_NAME = 'SiteTagSearches'; //Main program variable const STS = {}; //Setting values const BOORU_SITES = ['gelbooru', 'yandere', 'sankaku', 'konachan']; const SOURCE_SITES = ['pixiv', 'twitter', 'tumblr', 'deviantart', 'E-Hentai', 'nijie', 'artstation', 'fanbox', 'naver', 'lofter', 'skeb', 'tinami']; const CUSTOM_SITES_TOTAL = JSPLib.storage.checkLocalData('sts-custom-sites-total', { default_val: 5, validator: (_, num) => (Number.isInteger(num) && num >= 1 && num <= 20), }); //Main settings const SETTINGS_CONFIG = { booru_sites_enabled: { allitems: BOORU_SITES, reset: BOORU_SITES, validate: (data) => JSPLib.menu.validateCheckboxRadio(data, 'checkbox', BOORU_SITES), hint: "Select to show booru type." }, booru_sites_order: { allitems: BOORU_SITES, reset: BOORU_SITES, sortvalue: true, validate: (data) => JSPLib.utility.arrayEquals(data, BOORU_SITES), hint: "Set the order for how the booru sites appear in the tag popup." }, source_sites_enabled: { allitems: SOURCE_SITES, reset: SOURCE_SITES, validate: (data) => JSPLib.menu.validateCheckboxRadio(data, 'checkbox', SOURCE_SITES), hint: "Select to show source type." }, source_sites_order: { allitems: SOURCE_SITES, reset: SOURCE_SITES, sortvalue: true, validate: (data) => JSPLib.utility.arrayEquals(data, SOURCE_SITES), hint: "Set the order for how the source sites appear in the tag and other names popups." }, }; for (let index = 1; index <= CUSTOM_SITES_TOTAL; index++) { Object.assign(SETTINGS_CONFIG, { [`custom_site_${index}_enabled`]: { reset: false, validate: JSPLib.utility.isBoolean, hint: `Enable custom site ${index}.` }, [`custom_site_${index}_url`]: { reset: "", parse: String, validate: JSPLib.utility.isString, hint: `Tag URL for custom site ${index}.` }, [`custom_site_${index}_name`]: { reset: "", parse: String, validate: JSPLib.utility.isString, hint: `Entry name for custom site ${index}.` }, [`custom_site_${index}_icon`]: { reset: "", parse: String, validate: JSPLib.utility.isString, hint: `Icon URL for custom site ${index}. (optional)` } }); } const MENU_CONFIG = { topic_id: DANBOORU_TOPIC_ID, settings: [{ name: 'general', }, { name: 'source', }, { name: 'custom', message: "Additional sites that can be setup for the right-hand popup for both the main tags and the other names. They will always appear below the regular sites and according to their number.", }], controls: [], }; //CSS constants const PROGRAM_CSS = ` .sts-tag { border: 1px solid var(--default-border-color); padding: 2px 5px; display: flex; position: relative; text-align: center; } .sts-tag-text { display: inline-block; width: 100%; text-align: center; } .sts-links { position: absolute; background-color: var(--body-background-color); border: 1px solid var(--post-tooltip-border-color); display: none; z-index: 1; left: 50%; transform: translate(-50%); } .sts-links[data-type="source"] { top: 1.55em; } .sts-links[data-type="booru"] { bottom: 1.55em; } .sts-links ul.sts-link-list { padding: 5px; margin: 0; } .sts-links ul.sts-link-list li.sts-link { list-style-type: none; white-space: nowrap; } /*JQUERY-UI*/ .ui-icon { display: inline-block; height: 16px; width: 16px; } .ui-icon-triangle-1-e { background-position: -32px -14px; } .ui-icon-triangle-1-w { background-position: -100px -14px; } .ui-icon-triangle-1-n { background-position: -2px -14px; } .ui-icon-triangle-1-s { background-position: -64px -14px; }`; const MENU_CSS = ` .sts-selectors.jsplib-selectors label { width: 120px; }`; //HTML constants const CUSTOM_SITE_URL_DETAILS = `
Site URL format needs to include a %s where the tag should go.
For instance, the site URL for Pixiv is the following:
https://www.pixiv.net/en/tags/%s/artworks
The total number of custom sites available can be altered by setting the localStorage variable sts-custom-sites-total in the dev console (F12).
localStorage['sts-custom-sites-total'] = 10;
The default value is 5; the minimum value is 1; the maximum value is 20.
`; const NO_ICON = ''; //Site constants const SITE_CONFIG = { pixiv: { url: 'https://www.pixiv.net/en/tags/%s/artworks', icon: 'https://www.pixiv.net/favicon.ico', }, fanbox: { url: 'https://www.fanbox.cc/tags/%s', icon: 'https://www.fanbox.cc/favicon.ico', }, nijie: { url: 'https://nijie.info/search.php?word=%s', icon: 'https://nijie.info/icon/favicon.ico', }, tinami: { url: 'https://www.tinami.com/search/list?keyword=%s', icon: 'https://www.tinami.com/favicon.ico', }, deviantart: { url: 'https://www.deviantart.com/tag/%s', icon: 'https://www.deviantart.com/favicon.ico', }, artstation: { url: 'https://www.artstation.com/search?q=%s', icon: 'https://www.artstation.com/favicon.ico', }, tumblr: { url: 'https://www.tumblr.com/tagged/%s', icon: 'https://www.tumblr.com/favicon.ico', }, twitter: { url: 'https://x.com/hashtag/%s?src=hashtag_click&f=live', icon: 'https://abs.twimg.com/favicons/twitter.2.ico', }, 'E-Hentai': { url: 'https://e-hentai.org/?f_search=%s', icon: 'https://e-hentai.org/favicon.ico', }, naver: { url: 'https://section.blog.naver.com/Search/Post.naver?keyword=%s', icon: 'https://section.blog.naver.com/favicon.ico', }, lofter: { url: 'https://www.lofter.com/tag/%s', icon: 'https://www.lofter.com/favicon.ico', }, skeb: { url: 'https://skeb.jp/search?q=%s', icon: 'https://fcdn.skeb.jp/assets/v1/commons/favicon.ico', }, gelbooru: { url: 'https://gelbooru.com/index.php?page=post&s=list&tags=%s', icon: 'https://gelbooru.com/favicon.ico', }, yandere: { url: 'https://yande.re/post?tags=%s', icon: 'https://yande.re/favicon.ico', }, sankaku: { url: 'https://chan.sankakucomplex.com/?tags=%s', icon: 'https://chan.sankakucomplex.com/favicon.ico', }, konachan: { url: 'https://konachan.com/post?tags=%s', icon: 'https://konachan.com/favicon.ico', }, }; /***Functions***/ //Helper functions function GetWikiName() { if (STS.controller === 'posts') { let wiki_name = ""; let url = $('#show-excerpt-link').attr('href'); let match = url.match(/^\/wiki_pages\/([^/]+)/); if (match) { wiki_name = match[1]; } return decodeURIComponent(wiki_name); } if (STS.controller === 'wiki-pages') { return $('#wiki-page-title a').html().replace(/ /g, '_'); } } function AnySiteEnabled(type) { return Boolean(STS.user_settings[`${type}_sites_enabled`].length); } function IsSiteEnabled(site, type) { return STS.user_settings[`${type}_sites_enabled`].includes(site); } function GetCustomSetting(setting, index) { return STS.user_settings[`custom_site_${index}_${setting}`]; } //Render functions function RenderSiteLinks(type, searchtag, num) { let site_list = STS.user_settings[`${type}_sites_order`].map((site) => { if (!IsSiteEnabled(site, type)) return; let url = JSPLib.utility.sprintf(SITE_CONFIG[site].url, searchtag); let display_name = JSPLib.utility.displayCase(site); let icon = (SITE_CONFIG[site].icon ? `