Παλαιά: v0.99.61 - 05/03/2022 -
readd missing thumbnail padding (so thumbnail icons don't jump around)
change the modified border color priorities to what it was before
Νέα: v0.99.66 - 06/03/2022 - the cursed update:
add cool new SVG thumbnail icons!
allow editing posts on the deletion page
deletion page: add critial tags below their thumbnail (and also check for potential_upscale tag)
deletion page: check resolutions for exact integer multiples (potential upscale)
make beta link more useful by direct linking to appropriate pages (thanks to Octopus Hugger!)
add option for post border style: prioritizing blue "unapproved" over yellow/green "has parent/children" or vice versa
select tag script templates using 1-0 hotkeys
fix bug where "Edit Tags" mode would resubmit previously submitted data
- @@ -3,7 +3,7 @@
- // @namespace SankakuAddon
- // @description Adds a few quality of life improvements on Sankaku Channel: Automatic image scaling, scrolling to image, thumbnail icons for loud/animated posts, muting/pausing videos, + - tag search buttons, a tag menu which allows for tagging by clicking, 'Choose/Set Parent' modes, easier duplicate tagging/flagging. Fully configurable through the Addon config.
- // @author sanchan
-// @version 0.99.61
- +// @version 0.99.66
- // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABbmlDQ1BpY2MAACiRdZHPKwRhGMc/dolYbeIgOewBOeyWKDlqFZflsFZZXGZmZ3fVzphmZpNclYuDchAXvw7+A67KlVKKlOTgL/DrIo3n3VUr8U7vPJ++7/t9et/vC6FUybC8+gGwbN9NTyRjs9m5WOMTETpoox00w3Mmp8cz/Dveb6hT9Tqhev2/78/RkjM9A+qahIcNx/WFR4VTy76jeEO4wyhqOeF94bgrBxS+ULpe5UfFhSq/KnYz6TEIqZ6xwg/Wf7BRdC3hfuEeq1Q2vs+jbhIx7ZlpqV0yu/FIM0GSGDplFinhk5BqS2Z/+wYqvimWxGPI32EFVxwFiuKNi1qWrqbUvOimfCVWVO6/8/TyQ4PV7pEkNDwEwUsvNG7B52YQfBwEwechhO/hzK75lySnkTfRN2tazx5E1+DkvKbp23C6Dp13juZqFSksM5TPw/MxtGah/Qqa56tZfa9zdAuZVXmiS9jZhT7ZH134ArhcZ+m/WStSAAAACXBIWXMAAAsSAAALEgHS3X78AAAAeElEQVQ4y2NgoCX4Xyb7H4TxqWHCo7keG5toA4CgAQebsAHYbMTlCiYibMfrCiZibcIlx0SE3/GGBRM+Gxi7HjeCMD41TESGPE5XMOGzHRsbnxcIxbsDDAMts4cbjmR7A4kpvQHkMiZCKY1QSmXBZTKedNBA1RwLAFCeNCTVhz2FAAAAAElFTkSuQmCC
- // @match https://chan.sankakucomplex.com/*
- // @match https://idol.sankakucomplex.com/*
- @@ -25,7 +25,89 @@
- (async function(unsafeWindow) {
- 'use strict';
-
- const VERSION = 'v0.99.61';
- + const VERSION = 'v0.99.66';
- +
- + const SVG_SIZE = 20;
- + const SPEAKER_SVG = `<svg width="${SVG_SIZE}" height="${SVG_SIZE}" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
- + <rect x="2" y="2" width="28" height="28" fill-opacity=".67" fill-rule="evenodd" stroke-width=".22631"/>
- + <g fill="#fff" fill-opacity=".33" fill-rule="evenodd">
- + <rect width="32" height="2" stroke-width=".074927"/>
- + <rect x="30" y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect y="30" width="32" height="2" stroke-width=".07698"/>
- + </g>
- + <path d="m19 11c6 5 0 10 0 10" fill="none" stroke="#ff761c" stroke-width="2"/>
- + <path d="m23 9c8 7 0 14 0 14" fill="none" stroke="#ff761c" stroke-width="2"/>
- + <path d="m16 23h-3l-3-3h-5v-8h5l3-3h3" fill="#ff761c"/>
- +</svg>`;
- +
- + const ANIMATED_SVG = `<svg width="${SVG_SIZE}" height="${SVG_SIZE}" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
- + <rect x="2" y="2" width="28" height="28" fill-opacity=".67" fill-rule="evenodd" stroke-width=".22631"/>
- + <g fill="#fff" fill-opacity=".33" fill-rule="evenodd">
- + <rect width="32" height="2" stroke-width=".074927"/>
- + <rect x="30" y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect y="30" width="32" height="2" stroke-width=".07698"/>
- + </g>
- + <path d="m18 16-10 6v-12" fill="#ff761c"/>
- + <path d="m26 16-9 6v-12" fill="#ff761c"/>
- +</svg>`;
- +
- + const EXPLICIT_SVG = `<svg width="${SVG_SIZE}" height="${SVG_SIZE}" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
- + <g>
- + <rect x="2" y="2" width="28" height="28" fill-opacity=".67" fill-rule="evenodd" stroke-width=".22631"/>
- + <g fill="#fff" fill-opacity=".33" fill-rule="evenodd">
- + <rect x="-6.0956e-8" y="-1.3512e-9" width="32" height="2" stroke-width=".074927"/>
- + <rect x="30" y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect x="-6.0956e-8" y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect x="-6.0956e-8" y="30" width="32" height="2" stroke-width=".07698"/>
- + </g>
- + <g transform="scale(.9953 1.0047)" fill="#f99" aria-label="S">
- + <rect x="12.057" y="5.9718" width="11.052" height="2.9859" stroke-width="1.0856"/>
- + <rect x="12.057" y="22.892" width="11.052" height="2.9859" stroke-width="1.0856"/>
- + <rect x="12.057" y="14.432" width="9.0425" height="2.9859" stroke-width=".98198"/>
- + <rect x="9.0425" y="5.9718" width="3.0142" height="19.906" stroke-width="1.0541"/>
- + </g>
- + </g>
- +</svg>`;
- +
- + const QUESTIONABLE_SVG = `<svg width="${SVG_SIZE}" height="${SVG_SIZE}" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
- + <g>
- + <rect x="2" y="2" width="28" height="28" fill-opacity=".67" fill-rule="evenodd" stroke-width=".22631"/>
- + <g fill="#fff" fill-opacity=".33" fill-rule="evenodd">
- + <rect x="-6.0956e-8" y="-1.3512e-9" width="32" height="2" stroke-width=".074927"/>
- + <rect x="30" y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect x="-6.0956e-8" y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect x="-6.0956e-8" y="30" width="32" height="2" stroke-width=".07698"/>
- + </g>
- + <g transform="translate(6.9551 5.0704)" fill="#999" aria-label="S">
- + <path d="m11.545 14.93 5 4-2 2-5-4" fill="#999"/>
- + </g>
- + <text x="-13" y="4" font-family="'Andika New Basic'" font-size="40px" style="line-height:1.25" xml:space="preserve"><tspan x="-13" y="4"/></text>
- + <path d="m16 6.5a7 9.5 0 0 0-7 9.5 7 9.5 0 0 0 7 9.5 7 9.5 0 0 0 7-9.5 7 9.5 0 0 0-7-9.5zm0 1.5a5 8 0 0 1 5 8 5 8 0 0 1-5 8 5 8 0 0 1-5-8 5 8 0 0 1 5-8z" fill="#999"/>
- + </g>
- +</svg>`;
- +
- + const SAFE_SVG = `<svg width="${SVG_SIZE}" height="${SVG_SIZE}" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
- + <g>
- + <rect x="2" y="2" width="28" height="28" fill-opacity=".67" fill-rule="evenodd" stroke-width=".22631"/>
- + <g fill="#fff" fill-opacity=".33" fill-rule="evenodd">
- + <rect x="-6.0956e-8" y="-1.3512e-9" width="32" height="2" stroke-width=".074927"/>
- + <rect x="30" y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect x="-6.0956e-8" y="2" width="2" height="28" stroke-width=".075839"/>
- + <rect x="-6.0956e-8" y="30" width="32" height="2" stroke-width=".07698"/>
- + </g>
- + <g transform="scale(.9953 1.0047)" fill="#9f9" aria-label="S">
- + <path d="m21.586 9.7289q-0.78306-0.39548-1.4501-0.65914-0.65255-0.27684-1.2761-0.44822-0.62355-0.18456-1.2471-0.26366-0.62355-0.079096-1.3196-0.079096-0.87006 0-1.5951 0.23729-0.71055 0.23729-1.2326 0.63277-0.52204 0.39548-0.81206 0.90961-0.27552 0.51413-0.27552 1.0546 0 0.5405 0.15951 0.98871 0.17401 0.43503 0.68155 0.83052 0.50754 0.3823 1.4501 0.7646 0.94257 0.36912 2.4942 0.77779 1.4356 0.3823 2.5377 0.89643 1.1166 0.50095 1.8706 1.1601t1.1456 1.5028q0.39153 0.8437 0.39153 1.9115 0 1.3315-0.58004 2.4256-0.56554 1.081-1.5661 1.872-0.98607 0.77779-2.3057 1.2128-1.3196 0.42185-2.7987 0.42185-1.0441 0-2.0447-0.13183-0.98607-0.13183-1.8706-0.36912-0.87006-0.23729-1.6096-0.55368-0.73955-0.32957-1.2906-0.72506l0.65255-2.7025q1.4501 1.1469 2.9872 1.661 1.5516 0.51413 3.1757 0.51413 0.87006 0 1.6821-0.22411t1.4356-0.64596q0.62355-0.43503 1.0006-1.0546 0.37703-0.63278 0.37703-1.4369 0-0.51413-0.18851-0.97553-0.17401-0.47458-0.72505-0.9228-0.55104-0.44822-1.5806-0.88325-1.0296-0.44822-2.7262-0.90961-1.6966-0.4614-2.7697-1.0151-1.0731-0.55368-1.6821-1.1996-0.60904-0.65914-0.84106-1.3974-0.21752-0.75142-0.21752-1.5951 0-0.8437 0.37703-1.7797 0.39153-0.93598 1.2181-1.7269 0.82656-0.79097 2.1172-1.3051 1.2906-0.52731 3.1032-0.52731 0.84106 0 1.5661 0.065914 0.73956 0.065914 1.4211 0.21092 0.69605 0.14501 1.3631 0.36912 0.66705 0.22411 1.3776 0.5405z" fill="#9f9"/>
- + </g>
- + </g>
- +</svg>`;
- +
- + const RATING_SVG = {
- + e: EXPLICIT_SVG,
- + q: QUESTIONABLE_SVG,
- + s: SAFE_SVG,
- + };
-
- // based on the Tag Checklist in the wiki
- const DEFAULT_TAGLIST =
- @@ -474,13 +556,17 @@
- applied_css = true;
-
- // change priority of post borders (by redefining their colors after their original definition)
- // before: flagged < has-children < has-parent < pending < deleted
- // after: pending < deleted < has-children < has-parent < flagged
- // note: deleted posts only show through explicit search so they don't need a border
- // TODO maybe pending should be above has-children and has-parent?
- + // original: flagged < has-children < has-parent < pending < deleted
- + // default: deleted < has-children < has-parent < pending < flagged
- + // variants: pending < deleted < has-children < has-parent < flagged
- + // note: pending, flagged and deleted are mutually exclusive, so this is only about their relation to has-children/parent
- + // also note: deleted posts only show through explicit search so they don't really need a border
- add_style(`
- img.has-children { border-color: #A7DF38; }
- img.has-parent { border-color: #CCCC00; }
- + ${config.post_border_style === 0 ? `
- + img.pending { border-color: #4B4BA3; }
- + ` : ''}
- img.flagged { border-color: #F00; }
- `);
-
- @@ -501,19 +587,28 @@
- overflow: auto; /* fit its content */
- }
-
- /* balance the vertical margins */
- + /* balance margins */
- + #content > .deleting-post {
- + padding-top: 1em;
- + padding-bottom: 1em;
- + }
- #content > .deleting-post > div {
- margin-top: unset !important;
- margin-bottom: unset !important;
- }
- #content > .deleting-post > ul {
- margin-top: 1em;
- + margin-bottom: unset;
- }
-
- /* align first thumbnail with comparison box */
- #content > .thumb {
- margin-left: calc(4em + 4px);
- }
- +
- + /* adjustment to center tags below thumbnails */
- + #content .deleting-post .thumb > * {
- + margin: auto;
- + }
- `);
-
- /* replace thumbnail centering logic (more robust, needed for thumbnail icons) */
- @@ -522,7 +617,7 @@
- display: grid !important;
- place-content: center;
- }
-
- .thumb .preview {
- position: static !important;
- }
- @@ -618,6 +713,7 @@
- show_speaker_icon: true,
- show_animated_icon: true,
- show_ratings_icon: false,
- + post_border_style: 0,
- setparent_deletepotentialduplicate: false,
- editform_deleteuselesstags: false,
- hide_headerlogo: false,
- @@ -686,6 +782,7 @@
- tag_menu_layout: Number,
- tag_category_collapser_style: Number,
- highres_limit: Number,
- + post_border_style: Number,
- };
-
- function fix_config_entry(key, value) {
- @@ -887,6 +984,7 @@
- 'show_animated_icon',
- 'show_ratings_icon',
- 'view_history_enabled',
- + 'post_border_style',
- 'hide_headerlogo',
- 'sankaku_channel_dark_compatibility',
- ],
- @@ -932,10 +1030,11 @@
- video_controls: {type: 'checkbox', desc: 'Show video controls*'},
- tag_search_buttons: {type: 'checkbox', desc: 'Enable + - tag search buttons*'},
- or_tag_search_button: {type: 'checkbox', desc: 'Also add ~ tag search button*'},
- show_speaker_icon: {type: 'checkbox', desc: 'Show 🔊 icon on thumbnail if it has audio*'},
- show_animated_icon: {type: 'checkbox', desc: 'Show ⏩ icon on thumbnail if it is animated (🔊 overrides ⏩)*'},
- show_ratings_icon: {type: 'checkbox', desc: 'Show ratings icon (S, Q, E) on post thumbnails*'},
- + show_speaker_icon: {type: 'checkbox', desc: `Show ${SPEAKER_SVG} icon on thumbnail if it has audio*`},
- + show_animated_icon: {type: 'checkbox', desc: `Show ${ANIMATED_SVG} icon on thumbnail if it is animated (${SPEAKER_SVG} overrides ${ANIMATED_SVG} )*`},
- + show_ratings_icon: {type: 'checkbox', desc: `Show ratings icon (${SAFE_SVG}, ${QUESTIONABLE_SVG}, ${EXPLICIT_SVG}) on post thumbnails*`},
- view_history_enabled: {type: 'checkbox', desc: 'Fade out thumbnails of viewed posts (enables post view history)*'},
- + post_border_style: {type: 'select', desc: 'Post border style: ', options: {0: 'Prioritize blue \'unapproved\' border', 1: 'Priorizite yellow/green \'has parent / children\' borders'}},
- setparent_deletepotentialduplicate: {type: 'checkbox', desc: 'Delete potential_duplicate tag when using "Set Parent"'},
- editform_deleteuselesstags: {type: 'checkbox', desc: '"Save changes" button deletes useless_tags tag (if there have been changes)'},
- tag_category_collapser: {type: 'checkbox', desc: 'Enable tag category collapsers on post pages*'},
- @@ -1298,7 +1397,7 @@
- }
-
- innerDivHTML += '<div style="padding: 2px">';
- innerDivHTML += '<button id="config_close" style="cursor: pointer;">Close</button>';
- + innerDivHTML += '<button id="config_close" style="cursor: pointer; margin-right: 6px">Close</button>';
- innerDivHTML += '<button id="config_reset" style="cursor: pointer;" title="Resets all settings to default (but doesn\'t clear post history)">Reset settings</button>';
- innerDivHTML += '<button id="history_clear" style="cursor: pointer;" title="Clears the post view history for the current site (chan or idol)">Clear post view history</button>';
- innerDivHTML += '</div>';
- @@ -1306,6 +1405,12 @@
-
- cfg_dialog.innerHTML = innerDivHTML;
-
- + // adjust inline SVG icons
- + for (const svg of cfg_dialog.querySelectorAll('svg')) {
- + svg.style.width = '1rem';
- + svg.style.verticalAlign = 'middle';
- + }
- +
- document.body.appendChild(cfg_dialog);
-
- // hide non-default categories
- @@ -1653,8 +1758,8 @@
- collapse_tag_category(category, true, false);
- }
-
- function get_thumbnail_post_id(thumb_span) {
- const pid = thumb_span.id;
- + function get_thumbnail_post_id(thumb) {
- + const pid = thumb.id;
- if (typeof pid === 'undefined') return null; // first thumbnail on similar page doesn't have post id
- if (typeof pid !== 'string' || !pid.startsWith('p')) {
- console.error('[addon error] invalid thumbnail id');
- @@ -1670,14 +1775,52 @@
- return id;
- }
-
- // should be equivalent to unsafeWindow.Post.posts.get(get_thumbnail_post_id(thumb_span)).match_tags,
- // except unsafeWindow.Post might not yet be loaded.
- function get_thumbnail_tags(thumb_span) {
- const img = thumb_span.querySelector('.preview');
- + const post_cache = new Map(); // returns object with tags and rating
- + function get_post(post_id) {
- + // might not be loaded yet or exist at all (e.g on deletion page)
- + let post = unsafeWindow.Post?.posts?.get(post_id);
- + if (post !== undefined) return post;
- +
- + post = post_cache.get(post_id);
- + if (post !== undefined) return post;
- +
- + return null;
- + }
- +
- + function get_post_from_thumb(thumb) {
- + const img = thumb.querySelector('.preview');
- if (img === null) return null;
-
- const tags = img.title.trim().split(/\s+/);
- return tags;
- + const post_id = get_thumbnail_post_id(thumb);
- + let post = get_post(get_thumbnail_post_id(thumb));
- +
- + if (post !== null) return post;
- +
- + // note: thumbnail title tags are slightly different, e.g. Rating:Safe instead of rating:s
- + let tags = img.title.trim().split(/\s+/);
- +
- + // find rating
- + let rating = null;
- + for (const tag of tags) {
- + if (tag.startsWith('Rating:')) {
- + rating = tag.substring('Rating:'.length)[0].toLowerCase();
- + break;
- + }
- + }
- +
- + // remove "match tags"
- + tags = tags.filter((tag) => {
- + return !(tag.startsWith('Rating:') || tag.startsWith('Score:') || tag.startsWith('Size:') || tag.startsWith('User:'));
- + });
- +
- + post = {
- + tags,
- + rating,
- + };
- +
- + post_cache.set(post_id, post);
- +
- + return post;
- }
-
- function modify_thumbnails(root) {
- @@ -1687,14 +1830,10 @@
-
- for (const thumb of root.getElementsByClassName('thumb')) {
- const id = get_thumbnail_post_id(thumb);
- if (id === null) {
- show_notice(console.error, '[addon error] invalid thumbnail id?!');
- return;
- }
-
- + if (id === null) return;
-
- // use and update thumbnail_cache
- let thumbs = thumbnail_cache.get(id);
- if (thumbs === undefined) thumbs = [];
- + const thumbs = thumbnail_cache.get(id) ?? [];
-
- const is_new = !thumbs.includes(thumb);
-
- @@ -1702,8 +1841,7 @@
- thumbnail_cache.set(id, thumbs);
-
- if (is_new) {
- add_speaker_icon(thumb);
- add_ratings_icon(thumb);
- + add_thumbnail_icons(thumb);
- add_thumbnail_click_listener(thumb);
-
- if (!is_personal_post_page())
- @@ -1712,89 +1850,50 @@
- }
- }
-
- function add_thumbnail_click_listener(thumb_span) {
- const a = thumb_span.querySelector('a');
- if (a === null) return;
-
- a.addEventListener('click', (e) => {
- + function add_thumbnail_click_listener(thumb) {
- + thumb.querySelector('a')?.addEventListener('click', (e) => {
- thumbnail_click_listener(e.currentTarget.parentElement);
- });
- }
-
- function add_speaker_icon(thumb_span) {
- if (!(config.show_speaker_icon || config.show_animated_icon)) return;
- + function add_thumbnail_icons(thumb) {
- + if (!(config.show_speaker_icon || config.show_animated_icon || config.show_ratings_icon)) return;
-
- const a = thumb_span.querySelector('a');
- + const a = thumb.querySelector('a');
- if (a === null) return;
-
- const tags = get_thumbnail_tags(thumb_span);
- if (tags === null) return;
-
- const icon = document.createElement('SPAN');
- if (config.show_speaker_icon && (tags.includes('has_audio'))) {
- icon.innerText = '🔊';
- } else if (config.show_animated_icon && (tags.includes('animated') || tags.includes('video') || tags.includes('slideshow'))) {
- icon.innerText = '⏩';
- } else {
- return;
- }
-
- icon.className = 'speaker_icon';
- icon.style.color = '#666';
- icon.style.position = 'absolute';
- icon.style.top = '2px'; // account for border
- icon.style.right = '2px';
- icon.style.fontSize = '200%';
- icon.style.textShadow = '-1px 0 white, 0 1px white, 1px 0 white, 0 -1px white';
- icon.style.transform = 'translateX(50%) translateY(-50%)';
-
- a.style.display = 'inline-block'; // makes the element fit its content
- a.style.position = 'relative';
- a.appendChild(icon);
- }
-
- + const post = get_post_from_thumb(thumb);
- + if (post == null) return;
- +
-
- function add_ratings_icon(thumb_span) {
- if (!config.show_ratings_icon) return;
- + const icons = document.createElement('SPAN');
- + icons.style.whiteSpace = 'nowrap';
-
- const a = thumb_span.querySelector('a');
- if (a === null) return;
-
- const tags = get_thumbnail_tags(thumb_span);
- if (tags === null) return;
- + if (config.show_ratings_icon) {
- + icons.insertAdjacentHTML('beforeend', RATING_SVG[post.rating]);
- + }
-
- const icon = document.createElement('SPAN');
- if (tags.includes('Rating:Explicit')) {
- icon.innerText = 'E';
- icon.style.color = '#E00';
- } else if (tags.includes('Rating:Questionable')) {
- icon.innerText = 'Q';
- icon.style.color = '#666';
- } else if (tags.includes('Rating:Safe')) {
- icon.innerText = 'S';
- icon.style.color = '#4dc919';
- } else {
- return;
- + if (config.show_speaker_icon && (post.tags.includes('has_audio'))) {
- + icons.insertAdjacentHTML('beforeend', SPEAKER_SVG);
- + } else if (config.show_animated_icon && (post.tags.includes('animated') || post.tags.includes('video') || post.tags.includes('slideshow'))) {
- + icons.insertAdjacentHTML('beforeend', ANIMATED_SVG);
- }
-
- icon.className = 'ratings_icon';
- icon.style.position = 'absolute';
- icon.style.bottom = '2px'; // account for border
- icon.style.right = '2px';
- icon.style.fontSize = '200%';
- icon.style.textShadow = '-1px 0 white, 0 1px white, 1px 0 white, 0 -1px white';
- icon.style.transform = 'translateX(50%) translateY(50%)';
- + icons.className = 'thumbnail_icons';
- + icons.style.position = 'absolute';
- + icons.style.top = '2px'; // account for border
- + icons.style.right = '2px';
- + icons.style.transform = `translateX(${SVG_SIZE / 2}px) translateY(-${SVG_SIZE / 2}px)`;
-
- a.style.display = 'inline-block'; // makes the element fit its content
- a.style.position = 'relative';
- a.appendChild(icon);
- + a.appendChild(icons);
- }
-
- // TODO this isn't optimal when the thumbnail has low contrast to the background
- function fadeout_post(thumb_span) {
- + function fadeout_post(thumb) {
- if (!config.view_history_enabled) return;
-
- const a = thumb_span.querySelector('a');
- const img = thumb_span.querySelector('img');
- + const a = thumb.querySelector('a');
- + const img = thumb.querySelector('img');
-
- // move box shadow from image to link, so opacity doesn't affect it
- a.style.display = 'inline-block';
- @@ -1802,13 +1901,13 @@
- img.style.removeProperty('box-shadow');
-
- img.style.opacity = '20%';
- for (const speaker_icon of thumb_span.getElementsByClassName('speaker_icon'))
- + for (const speaker_icon of thumb.getElementsByClassName('speaker_icon'))
- speaker_icon.style.opacity = '20%';
- }
-
- function fadeout_viewed_post(thumb_span, id) {
- + function fadeout_viewed_post(thumb, id) {
- if (config[HISTORY_KEY].has(id))
- fadeout_post(thumb_span);
- + fadeout_post(thumb);
- }
-
- function add_thumbnail_observer(predicate) {
- @@ -1831,6 +1930,40 @@
- node.controls = config.video_controls;
- }
-
- + function useful_beta_link(pathname) { // idea and some code donated by Octopus Hugger
- + const params = new URL(window.location.href).searchParams;
- + const betaLink = new URL('https://beta.sankakucomplex.com/');
- +
- + // normalize post index
- + if (pathname.startsWith('/post/index')) pathname = '/';
- +
- + // post index/page, simply replace chan with beta and keep search parameters
- + if (pathname === '/' || pathname.startsWith('/post/show')) {
- + betaLink.pathname = pathname;
- + betaLink.search = window.location.search;
- + }
- +
- + // user page, /user/show/<id> -> /user/show?id=<id>
- + if (pathname.startsWith('/user/show')) {
- + const temp = (pathname.endsWith('/') ? pathname.slice(0, -1) : pathname);
- + const user_id = temp.substring(temp.lastIndexOf('/') + 1);
- +
- + betaLink.pathname = '/user/show';
- + betaLink.searchParams.set('id', user_id);
- + }
- +
- + // wiki, /wiki/show?title=<entry> -> /tag/en?tagName=<entry>
- + if (pathname.startsWith('/wiki/show')) {
- + betaLink.pathname = '/tag/en';
- + betaLink.searchParams.set('tagName', params.get('title'));
- + }
- +
- + // update beta link
- + for (const a of document.querySelectorAll('#navbar a[href="https://beta.sankakucomplex.com/"]')) {
- + a.href = betaLink.href;
- + }
- + }
- +
-
- /***********************************************/
- /* main page / visually similar page functions */
- @@ -1916,14 +2049,17 @@
- <img id="SA-edit-image" src="${EMPTY_IMAGE}">
- </div>
- <div>
- +<h5><a id="post-link" href="" target="_blank"></a></h5>
- <form id="SA-edit-form" method="post" style="width: fit-content">
- <input id="SA-post_old_tags" name="post[old_tags]" type="hidden" value="">
- <table class="form">
- <tfoot>
- <tr>
-<td colspan="2"><input accesskey="s" name="commit" tabindex="11" type="submit" value="Save changes" style="min-width: 13.5em;">
- +<td colspan="2" style="white-space: nowrap"><input accesskey="s" name="commit" tabindex="11" type="submit" value="Save changes" style="min-width: 13.5em;">
- <button id="tag_reset_button" style="margin-left: 6px; cursor: pointer;">Reset</button>
-<button id="post-link" style="cursor: pointer;">Open Post</button>
- +<button id="tag_dup_button" style="cursor: pointer;">+duplicate</button>
- +<button id="tag_var_button" style="cursor: pointer;">+legitimate_variation</button>
- +<button id="tag_pot_button" style="cursor: pointer;">+potential_duplicate</button>
- </td>
- </tr>
- </tfoot>
- @@ -1946,7 +2082,7 @@
- <label class="block" for="SA-post_tags">Tags</label>
- </th>
- <td>
-<textarea cols="50" id="SA-post_tags" name="post[tags]" rows="9" spellcheck="false" tabindex="10" autocomplete="off"></textarea>
- +<textarea cols="82" id="SA-post_tags" name="post[tags]" rows="9" spellcheck="false" tabindex="10" autocomplete="off"></textarea>
- </td>
- </tr>
- </tbody>
- @@ -1983,23 +2119,20 @@
-
- function is_post_edit_dialog_visible() {
- const dialog = document.getElementById('post_edit_dialog');
- return dialog !== null && dialog.style.display === 'block';
- + return dialog?.style.display === 'block';
- }
-
- function open_post_edit_dialog(thumb_span) {
- + function open_post_edit_dialog(thumb) {
- const dialog = document.getElementById('post_edit_dialog');
- if (dialog === null) {
- show_notice(console.error, '[addon error] tag edit popup is missing?!');
- return;
- }
-
- const post_id = get_thumbnail_post_id(thumb_span);
- if (post_id === null) {
- show_notice(console.error, '[addon error] invalid thumbnail id?!');
- return;
- }
-
- + const post_id = get_thumbnail_post_id(thumb);
- + if (post_id === null) return;
-
- const a = thumb_span.querySelector('a');
- + const a = thumb.querySelector('a');
- if (a === null) return;
-
- const edit_image = document.getElementById('SA-edit-image');
- @@ -2010,16 +2143,14 @@
-
- // set thumbnail image and post link
- edit_image.src = EMPTY_IMAGE;
- edit_image.src = thumb_span.querySelector('.preview').src;
- post_link.onclick = () => {
- open_in_tab(a.href);
- return false;
- };
- + edit_image.src = thumb.querySelector('.preview').src;
- + post_link.href = a.href;
- + post_link.innerText = 'Post ' + post_id;
-
- const full_rating = (rating) => ({ s: 'safe', q: 'questionable', e: 'explicit' }[rating]);
-
- // get tags and rating (don't use get_thumbnail_tags() here because of possible browser extension conflicts)
- const post = unsafeWindow.Post.posts.get(post_id);
- + // get tags and rating
- + const post = get_post_from_thumb(thumb);
- const tags = post.tags.join(' ');
- const rating = full_rating(post.rating);
-
- @@ -2031,9 +2162,14 @@
- document.getElementById('SA-post_rating_' + rating).checked = true;
- }
-
- form.addEventListener('submit', (event) => {
- + update_tag_elements();
- +
- + form.onsubmit = (event) => {
- event.preventDefault(); // block reloading page
-
- + const submitted_tags = post_tags.value.trim().split(/\s+/);
- + show_notice(console.log, '[addon] saving...');
- +
- // manually submit data
- fetch(`/post/update/${post_id}`, {
- method: 'POST',
- @@ -2043,7 +2179,11 @@
- .then((result) => {
- // we assume success on redirect
- if (result.type === 'opaqueredirect' || result.ok) {
- show_post_edit_dialog(false);
- + // update local tags
- + post.tags = submitted_tags;
- + deletion_sanity_checks();
- +
- + show_notice(console.log, '[addon] saved tags!');
- } else {
- show_notice(console.error, '[addon error] couldn\'t save tags!');
- }
- @@ -2051,7 +2191,7 @@
- .catch((error) => {
- show_notice(console.error, '[addon error] network error while saving tags!', error);
- });
- });
- + };
-
- show_post_edit_dialog(true);
- }
- @@ -2059,20 +2199,27 @@
- function add_postmode_hotkeys() {
- document.addEventListener('keydown', (e) => {
- const mode_dropdown = document.getElementById('mode');
- + const script_presets = document.getElementById('tagscript_presets_dropdown');
- if (mode_dropdown === null) return;
- if (e.ctrlKey || e.altKey || e.shiftKey) return;
-
- if (e.target === mode_dropdown) {
- + if (e.target === mode_dropdown || (script_presets !== null && e.target === script_presets)) {
- e.preventDefault(); // e.g. 'v' would otherwise change to 'View Posts'
- } else {
- const tag = e.target.tagName.toLowerCase();
- if (tag === 'input' || tag === 'textarea' || tag === 'select') {
- return;
- }
-
- + } else if (['INPUT', 'TEXTAREA', 'SELECT'].includes(e.target.tagName)) {
- + return;
- }
-
- const old_mode = mode_dropdown.value;
-
- + // allow to quickly apply a tagscript preset
- + if ('0' <= e.key && e.key <= '9') {
- + const script_index = e.key === '0' ? 10 : Number(e.key);
- + const dropdown = document.getElementById('tagscript_presets_dropdown');
- + dropdown.selectedIndex = script_index;
- + dropdown.dispatchEvent(new Event('change'));
- + return;
- + }
- +
- switch (e.key) {
- case 'v':
- if (IS_GREASEMONKEY4) {
- @@ -2610,8 +2757,8 @@
- }
-
- function add_tags_change_listener() {
- const post_tags_area = document.getElementById('post_tags');
- if (post_tags_area === null) return; // not logged in
- + const post_tags = document.getElementById('post_tags') ?? document.getElementById('SA-post_tags');
- + if (post_tags === null) return; // not logged in
-
- const delayed_update = () => {
- tags_changed = true;
- @@ -2619,8 +2766,8 @@
- tag_update_timer = setTimeout(update_tag_elements, 500);
- };
-
- post_tags_area.addEventListener('change', delayed_update);
- post_tags_area.addEventListener('input', delayed_update);
- + post_tags.addEventListener('change', delayed_update);
- + post_tags.addEventListener('input', delayed_update);
- }
-
- // also used for 'tag_menu_save' button
- @@ -2806,40 +2953,45 @@
- }
-
- function update_tag_buttons() {
- const taglist = document.getElementById('post_tags');
- const dup_button = document.getElementById('tag_dup_button');
- const var_button = document.getElementById('tag_var_button');
- const pot_button = document.getElementById('tag_pot_button');
-
- if (taglist === null || dup_button === null || var_button === null || pot_button === null)
- + const taglist = document.getElementById('post_tags') ?? document.getElementById('SA-post_tags');
- + if (taglist === null)
- return;
-
- const tags = get_tags_array();
-
- if (!tags.includes('duplicate')) {
- dup_button.onclick = function() { add_tag('duplicate'); return false; };
- dup_button.innerText = 'Tag duplicate';
- } else {
- dup_button.onclick = function() { remove_tag('duplicate'); return false; };
- dup_button.innerText = 'Untag duplicate';
- + const dup_button = document.getElementById('tag_dup_button');
- + if (dup_button !== null) {
- + if (!tags.includes('duplicate')) {
- + dup_button.onclick = function() { add_tag('duplicate'); return false; };
- + dup_button.innerText = '+duplicate';
- + } else {
- + dup_button.onclick = function() { remove_tag('duplicate'); return false; };
- + dup_button.innerText = '-duplicate';
- + }
- +
- }
-
- if (!tags.includes('legitimate_variation')) {
- var_button.onclick = function() { add_tag('legitimate_variation'); return false; };
- var_button.innerText = 'Tag legitimate_variation';
- } else {
- var_button.onclick = function() { remove_tag('legitimate_variation'); return false; };
- var_button.innerText = 'Untag legitimate_variation';
- + const var_button = document.getElementById('tag_var_button');
- + if (var_button !== null) {
- + if (!tags.includes('legitimate_variation')) {
- + var_button.onclick = function() { add_tag('legitimate_variation'); return false; };
- + var_button.innerText = '+legitimate_variation';
- + } else {
- + var_button.onclick = function() { remove_tag('legitimate_variation'); return false; };
- + var_button.innerText = '-legitimate_variation';
- + }
- +
- }
-
- pot_button.innerText = 'Untag potential_duplicate';
- if (!tags.includes('potential_duplicate')) {
- pot_button.disabled = true;
- pot_button.style.removeProperty('cursor');
- } else {
- pot_button.onclick = function() { remove_tag('potential_duplicate'); return false; };
- pot_button.disabled = false;
- pot_button.style.cursor = 'pointer';
- + const pot_button = document.getElementById('tag_pot_button');
- + if (pot_button !== null) {
- + pot_button.innerText = '-potential_duplicate';
- + if (!tags.includes('potential_duplicate')) {
- + pot_button.disabled = true;
- + pot_button.style.removeProperty('cursor');
- + } else {
- + pot_button.onclick = function() { remove_tag('potential_duplicate'); return false; };
- + pot_button.disabled = false;
- + pot_button.style.cursor = 'pointer';
- + }
- +
- }
- }
-
- @@ -2848,14 +3000,17 @@
- }
-
- function get_old_tags_array() {
- return document.getElementById('post_old_tags').value.trim().split(/\s+/);
- + const post_old_tags = document.getElementById('post_old_tags') ?? document.getElementById('SA-post_old_tags');
- + return post_old_tags.value.trim().split(/\s+/);
- }
-
- function get_tags_array() {
- return document.getElementById('post_tags').value.trim().split(/\s+/);
- + const post_tags = document.getElementById('post_tags') ?? document.getElementById('SA-post_tags');
- + return post_tags.value.trim().split(/\s+/);
- }
-
- function add_tag(tag, skip_common_tags_update = false) {
- + const post_tags = document.getElementById('post_tags') ?? document.getElementById('SA-post_tags');
- const tags = get_tags_array();
-
- if ((tag === 'duplicate' && tags.includes('legitimate_variation')) || (tag === 'legitimate_variation' && tags.includes('duplicate'))) {
- @@ -2868,20 +3023,21 @@
- return;
- }
-
- document.getElementById('post_tags').value += ' ' + tag;
- + post_tags.value += ' ' + tag;
-
- tags_changed = true;
- update_tag_elements(skip_common_tags_update);
- }
-
- function remove_tag(tag, skip_common_tags_update = false) {
- + const post_tags = document.getElementById('post_tags') ?? document.getElementById('SA-post_tags');
- const tags = get_tags_array();
-
- for (let i = 0; i < tags.length; i++)
- if (tags[i] === tag)
- tags[i] = '';
-
- document.getElementById('post_tags').value = tags.join(' ').trim();
- + post_tags.value = tags.join(' ').trim();
-
- tags_changed = true;
- update_tag_elements(skip_common_tags_update);
- @@ -2896,7 +3052,9 @@
- }
-
- function reset_tags() {
- document.getElementById('post_tags').value = document.getElementById('post_old_tags').value;
- + const post_tags = document.getElementById('post_tags') ?? document.getElementById('SA-post_tags');
- + const post_old_tags = document.getElementById('post_old_tags') ?? document.getElementById('SA-post_old_tags');
- + post_tags.value = post_old_tags.value;
- tags_changed = false;
- update_tag_elements();
- }
- @@ -3666,13 +3824,51 @@
- if (thumbs.length !== 2)
- return; // no parent
-
- const checked_tags = ['upscaled', 'legitimate_variation', 'revision', 'third-party_edit'];
-
- + const WARNING_TEXT = '<b style="color: crimson">Warning: </b>';
- + const CHECKED_TAGS = ['upscaled', 'legitimate_variation', 'revision', 'third-party_edit', 'potential_upscale'];
- +
- + // remove old warnings before re-evaluating
- + document.querySelectorAll('.SA-warning').forEach((el) => el.remove());
- +
- + const h5 = document.querySelector('#content > .deleting-post > ul > h5');
- + const res = document.querySelector('#content > .deleting-post > ul > li:nth-child(2)');
- +
- + const posts = thumbs.map(get_post_from_thumb);
- + const warn_tags = posts.map((post) => post.tags.filter((tag) => CHECKED_TAGS.includes(tag)));
- +
- + const convert_to_margin = (thumb) => {
- + const img = thumb.querySelector('.preview');
- + const thumb_height = window.getComputedStyle(thumb).height;
- + const img_height = window.getComputedStyle(img).height;
- +
- + // the thumbnail image is centered in a grid (see adjust_css()), which will move it up when something is added below it
- + // to fix this we replace the vertical centering with a margin (and allow the thumbnail to scale vertically too)
- +
- + // fix thumbnail in place vertically
- + img.style.marginTop = `calc((${thumb_height} - ${img_height}) / 2 - 2px)`;
- + thumb.style.alignContent = 'start';
- + // make thumbnail scale vertically
- + thumb.style.minHeight = thumb_height;
- + thumb.style.height = 'auto';
- + };
- +
-
- const thumbs_tags = thumbs.map(get_thumbnail_tags);
- const thumb_warn_tags = thumbs_tags.map((tags) => tags.filter((tag) => checked_tags.includes(tag)));
- + thumbs.forEach(convert_to_margin);
-
- const warnings = [];
-
- + const add_tags_below_thumb = (thumb, tags, missing) => {
- + for (const tag of tags) {
- + const span = document.createElement('SPAN');
- + span.className = 'SA-warning';
- + span.style.color = 'crimson';
- + span.style.fontWeight = 'bold';
- + if (missing) span.style.textDecoration = 'line-through';
- + span.innerText = tag;
- +
- + thumb.appendChild(span);
- + }
- + };
- +
- const add_tag_warning = (post, tags) => {
- if (tags.length === 0) return;
-
- @@ -3681,22 +3877,53 @@
- warnings.push(`${post} has ${taglist} tag${tags.length > 1 ? 's' : ''}`);
- };
-
- add_tag_warning('child', thumb_warn_tags[0]);
- add_tag_warning('parent', thumb_warn_tags[1]);
- if (!thumbs_tags[0].includes('duplicate'))
- + for (let i = 0; i < thumbs.length; i++) {
- + add_tags_below_thumb(thumbs[i], warn_tags[i]);
- + }
- +
- + add_tag_warning('child', warn_tags[0]);
- + add_tag_warning('parent', warn_tags[1]);
- + if (!posts[0].tags.includes('duplicate')) {
- warnings.push('child isn\'t tagged as <b>duplicate</b>');
- + add_tags_below_thumb(thumbs[0], ['duplicate'], true);
- + }
-
- const h5 = document.querySelector('.deleting-post > ul > h5');
-
- + const integer_multiple = (a, b) => {
- + if (a > b && a % b === 0) {
- + return a / b;
- + }
- + return NaN;
- + };
- +
- + const widths = [];
- + const heights = [];
- +
- + // read resolutions
- + for (const b of res.getElementsByTagName('B')) {
- + const match = /([\d]+)x([\d]+)/.exec(b.innerText);
- + if (match) {
- + const [, width, height] = match;
- + widths.push(Number(width));
- + heights.push(Number(height));
- + }
- + }
- +
- + // add potential upscale warning
- + const multiple = integer_multiple(...widths);
- + if (multiple === integer_multiple(...heights)) {
- + res.insertAdjacentHTML('beforeend', ` ${WARNING_TEXT} potential ${multiple}x upscale`);
- + }
- +
-
- + // insert tag text warnings (TODO remove?)
- for (const warning of warnings.reverse()) {
- const li = document.createElement('LI');
- li.insertAdjacentHTML('beforeend', '<b style="color: crimson">Warning: </b>');
- li.insertAdjacentHTML('beforeend', warning);
- + li.className = 'SA-warning';
- + li.insertAdjacentHTML('beforeend', WARNING_TEXT + warning);
- h5.insertAdjacentElement('afterend', li);
- }
- }
-
- function add_custom_duplicate_delete_reason() { // and sanity checks
- + function add_custom_duplicate_delete_reason() {
- const reason = document.getElementById('reason');
- const custom_reason = document.getElementById('custom_reason');
-
- @@ -3744,9 +3971,28 @@
- tags_not_present.appendChild(button);
- }
-
- + function add_post_edit_buttons() {
- + // cache thumbnail tags
- + for (const thumb of document.querySelectorAll('.thumb'))
- + get_post_from_thumb(thumb);
- +
- + const thumbs = [...document.querySelectorAll('.deleting-post .thumb')];
- + for (const thumb of thumbs) {
- + const a = document.createElement('A');
- + a.innerText = '⚙';
- + a.style.fontSize = '120%';
- + a.href = '#';
- + a.onclick = () => {
- + open_post_edit_dialog(thumb);
- + return false;
- + };
- + thumb.appendChild(a);
- + }
- + }
- +
- function add_moderation_search_template() {
- const query = document.getElementById('query');
- const select = create_template_dropdown(new Map([['Flagged Posts', '-status:pending']]), (value) => {
- + const select = create_template_dropdown(new Map([['Pending Posts', 'status:pending'], ['Flagged Posts', '-status:pending']]), (value) => {
- select.selectedIndex = 0;
- query.focus();
-
- @@ -3869,6 +4115,7 @@
- update_config_dialog();
-
- update_headerlogo();
- + useful_beta_link(pathname);
-
- // process what the thumbnail observer may have missed
- modify_thumbnails(document);
- @@ -3882,6 +4129,7 @@
- if (config.tag_search_buttons) add_tag_search_buttons();
- add_postmode_hotkeys();
- add_post_edit_dialog();
- + update_tag_elements(); // initialize tag menu/buttons
-
- if (called_add_mode_options) {
- // add_mode_options() was called early, as it should
- @@ -3982,6 +4230,12 @@
- document.getElementById('custom_reason').style.minWidth = '25%';
- add_custom_duplicate_delete_reason();
- add_tags_copy_button();
- + add_post_edit_dialog();
- + add_post_edit_buttons();
- +
- + update_tag_elements(); // initialize tag menu/buttons
- + add_tags_change_listener();
- +
- deletion_sanity_checks();
-
- /*****************/