/**
* Hides NSFW images under a spoiler.
* This gadget can be used both for Wikimedia Commons and for other wiki projects.
*
* @author putnik, 2019-2020
*/
( function ( mw, $ ) {
var nsfwTopics = [
'Q291', // pornography
'Q496', // feces
'Q608', // human sexual activity
'Q5880', // vagina
'Q5887', // orgasm
'Q9103', // breast
'Q10791', // nudity
'Q10816', // sex toy
'Q40446', // nude
'Q42165', // buttocks
'Q124490', // violence
'Q133993', // erection
'Q174471', // scrotum
'Q181001', // erotica
'Q188641', // nipple
'Q650891', // glans penis
'Q673203', // foreskin
'Q843533', // areola
'Q844482', // killing
'Q1058795', // body fluid
'Q1406501', // labia
'Q2148678', // sexual penetration
'Q2192288', // vulva
'Q3258546', // human anus
'Q4620674', // sex organ
'Q11722446', // mons pubis
];
var imagesData = [];
var $images;
var isCommons = 'commonswiki' === mw.config.get( 'wgDBname' );
var commonsApi;
var foundTopics = [];
var init = function() {
commonsApi = isCommons ? new mw.Api() : new mw.ForeignApi( '//commons.wikimedia.org/w/api.php' );
findImages();
};
var findImages = function() {
$images = $( '.image:not(.nsfw)' ).filter( function() {
var $img = $( this ).find( 'img' );
return $img.width() * $img.height() >= 4000 && $img.attr( 'src' ).match( 'upload.wikimedia.org' );
} );
var imageTitles = $images.map( function() {
var src = $( this ).find( 'img' ).attr( 'src' );
return 'File:' + parseImageName( src );
} ).toArray();
for ( var offset = 0; offset < imageTitles.length; offset += 50 ) {
loadDepicts( imageTitles.slice( offset, offset + 50 ) );
}
};
var loadDepicts = function( imageTitles ) {
commonsApi.get( {
action: 'wbgetentities',
props: [ 'info', 'claims' ],
sites: 'commonswiki',
titles: imageTitles,
} ).done( function ( data ) {
if ( data.entities === undefined ) {
return;
}
for ( var pageMid in data.entities ) {
var entity = data.entities[ pageMid ];
if ( entity.statements === undefined || entity.statements.P180 === undefined ) {
continue;
}
imagesData.push( {
mid: pageMid,
title: entity.title,
depicts: entity.statements.P180.map( function( value ) {
return value.mainsnak.datavalue.value.id;
} ),
} );
}
findNsfwTopics();
} );
};
var findNsfwTopics = function() {
var topics = imagesData.map( function( imageData ) {
return imageData.depicts;
} ).flat();
topics = topics.filter( function( value, index, self ) {
return self.indexOf( value ) === index;
} );
var sparqlQuery = `SELECT ?depicts WHERE {
?depicts wdt:P31*/wdt:P279* ?nsfw .
VALUES ?depicts {wd:` + topics.join( ' wd:' ) + `}
VALUES ?nsfw {wd:` + nsfwTopics.join( ' wd:' ) + `}
}`;
$.ajax( {
url: 'https://query.wikidata.org/sparql?format=json&query=' + encodeURIComponent( sparqlQuery ),
success: function ( data ) {
if ( !data.results.bindings.length ) {
return;
}
for ( var i in data.results.bindings ) {
var row = data.results.bindings[ i ];
var topic = row.depicts.value.replace( 'http://www.wikidata.org/entity/', '' );
foundTopics.push( topic );
}
if ( foundTopics.length ) {
hideNsfwImages();
}
}
} );
};
var hideNsfwImages = function() {
mw.util.addCSS( `
.image.nsfw {
display: inline-block;
vertical-align: top;
position: relative;
overflow: hidden;
}
.image.nsfw>img {
filter: blur(20px);
}
.image.nsfw::before {
content: "NSFW";
position: absolute;
left: 50%;
top: 50%;
z-index: 1;
background: #fff;
border-radius: 2em;
height: 3em;
line-height: 3em;
width: 6em;
margin-left: -3em;
margin-top: -1.5em;
opacity: .6;
color: #222;
text-align: center;
}
.image.nsfw:hover::before {
opacity: .8;
}
` );
var imageNames = [];
var intersectsFoundTopics = function( value ) {
return -1 !== foundTopics.indexOf( value );
};
for ( var i in imagesData ) {
if ( imagesData[ i ].depicts.filter( intersectsFoundTopics ).length > 0 ) {
imageNames.push( imagesData[ i ].title.replace( /^File:/, '' ) );
}
}
$images = $( '.image' ).each( function() {
var $image = $( this );
var src = $image.find( 'img' ).attr( 'src' );
var name = parseImageName( src );
if ( -1 !== imageNames.indexOf( name ) ) {
$image.addClass( 'nsfw' );
}
} );
$( '.image.nsfw' )
.off( 'click' )
.one( 'click', onClick );
};
var parseImageName = function( src ) {
var name = src.replace( /\/(?:lossless\-page\d+\-|lang[a-z]+\-)?\d+px\-[^\/]+$/, '' );
name = name.replace( /^.*\/([^\/]+)$/, '$1' );
return decodeURIComponent( name.replace( /_/g, ' ' ) );
};
var onClick = function( e ) {
e.preventDefault();
$( this ).removeClass( 'nsfw' );
};
$.when(
$.ready,
mw.loader.using( [
( isCommons ? 'mediawiki.api' : 'mediawiki.ForeignApi' ),
'mediawiki.util',
] )
).done( init );
}( mediaWiki, jQuery ) );