User:Bargioni/UseAsRef 1.0.js

From Wikidata
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/* *******************************************************************
			 _   _           ___      ______      __ 
			| | | |         / _ \     | ___ \    / _|
			| | | |___  ___/ /_\ \___ | |_/ /___| |_ 
			| | | / __|/ _ \  _  / __||    // _ \  _|
			| |_| \__ \  __/ | | \__ \| |\ \  __/ |  
			 \___/|___/\___\_| |_/___/\_| \_\___|_|  
		
		    use an ID as a reference for statements
	gadget written by User:Epìdosis and User:Bargioni - feb 2021
******************************************************************* */

mw.loader.using('wikibase.Site').then(function () {
 ( function ( mw, wb, $ ) {

	function makeSPARQLQuery( endpointUrl, sparqlQuery, doneCallback ) {
		var settings = {
			headers: { Accept: 'application/sparql-results+json' },
			data: { query: sparqlQuery }
		};
		return $.ajax( endpointUrl, settings ).then( doneCallback );
	}
	
	function snaks_order (reference, P) {
		// output order ["P248", "P9073", "P1810", "P813"]
		var ordered_snaks = [];
		ordered_snaks.push('P248');
		ordered_snaks.push(P);
		if (reference.P1810) ordered_snaks.push('P1810');
		ordered_snaks.push('P813');
		return ordered_snaks;
	}
	
	function new_reference ( current_reference ) {
		var now = '+'+(new Date()).toISOString().substr(0,10)+'T00:00:00Z'; // WD doesn't handle hhmmss
		var source_qid = current_reference.P9073.substr(1); // the numeric part of Qnnnn
		var reference = {
			"P248": [
				{
					"snaktype": "value",
					"property": "P248", // source of info
					"datavalue": {
						"value": {
							"entity-type": "item",
							"numeric-id": source_qid
						},
						"type": "wikibase-entityid"
					}
				}
			],
			[current_reference.P] : [
				{
					"snaktype": "value",
					"property": current_reference.P,
					"datavalue": {
						"value": current_reference.value,
						"type": "string"
					},
					"datatype": "external-id"
				}
			],
			"P813": [
				{
					"snaktype": "value",
					"property": "P813", // stated on
					"datatype": "time",
					"datavalue": {
						"value": {
							"after": 0,
							"before": 0,
							"calendarmodel": "http://www.wikidata.org/entity/Q1985727",
							"precision": 11,
							"time": now,
							"timezone": 0
						},
						"type": "time"
					}
				}
			]
		};
		if (current_reference.P1810) {
			reference.P1810 = [
				{
					"snaktype": "value",
					"property": "P1810",
					"datavalue": {
						"value": current_reference.P1810,
						"type": "string"
					},
					// "datatype": "?string?" // not mandatory
				}
			];
		}
		return reference;
	}

	function append_reference_to_statement( paste_icon, claim_guid, NR, P ) {
		// disable paste icon, without changing its visibility
		// perform only after a successfull save? FIXME
		var so = snaks_order( NR, P );
		$(paste_icon)
			.css({
				'border':'3px solid red',
				'padding':'2px'
			});
		$(paste_icon).parents('.pasteref-container').css('pointer-events','none');
		// save new reference
		var api = new mw.Api();
	    var token = mw.user.tokens.values.csrfToken;
	    api.post({ 
			'action'     : 'wbsetreference',
			'summary'    : 'Added with [[User:Bargioni/UseAsRef|UseAsRef]]',
			'statement'  : claim_guid,
			'snaks-order': JSON.stringify(so),
			'snaks'      : JSON.stringify(NR),
			'format'     : 'json',
			'token'      : token
		})
		.then( function(aw) {
			if ( aw.success == 1 ) {
				var ps = aw.reference['snaks-order'].toString();
				mw.notify ('Reference successfully added using '+ps+'.\nYou may wish to reload the page.',
					{
						title: 'UseAsRef - Info',
						autoHide: true,
						type: 'info'
					}
				);
			}
			else {
				console.log('ko',aw);
				mw.notify (aw.error,
					{
						title: 'UseAsRef - Error',
						autoHide: false,
						type: 'error'
					}
				);
			}
		} )
		.catch( function(e) { // catch errors that may occur inside .then function
			console.log('ko',e);
			mw.notify (e,
				{
					title: 'UseAsRef - Error',
					autoHide: false,
					type: 'error'
				}
			);
		} );
	}
	
	function paste_reference ( paste_icon, current_reference ) {
		// console.log( 'paste_reference', current_reference ); // test
		// console.log( 'paste_icon', paste_icon ); // test
		var in_edit_mode = $(paste_icon).parents('.wikibase-statementview-mainsnak').parent().parent().find('.wikibase-edittoolbar-ineditmode').length;
		if (in_edit_mode) {
			$('#useasref_dialog_msg').html('<div style="color:red">Cannot paste when in edit mode</div>');
			$( '#useasref_dialog' ).dialog( 'open' );
			return;
		}
		var NR = new_reference( current_reference );
		var qid = mw.config.get( 'wbEntityId' ); // Qnnnn unused? TODO
		var P = $(paste_icon).parents('div[id^=P]');
		var Pclaim = $(P).attr('id'); // this is the P of the claim(s) the reference will be attached
		var claim_guid = $(paste_icon).parents('div[class~="wikibase-statementview"]').attr('id');
		var claim = Pclaim + ':'+ claim_guid; // unused? TODO
		// and finally, let's call wbsetreference; see https://www.wikidata.org/w/api.php?action=help&modules=wbsetreference
		append_reference_to_statement( paste_icon, claim_guid, NR, current_reference.P );
	}
	
	function link_to_P (P) {
		var L = '<a href="https://www.wikidata.org/wiki/Property:'+ P +'" target"="_blank">'+P+'</a>';
		return L;
	}
	
	function doneCallback(R) {
		// console.log(R, mw.current_reference); // test
		var rrb = R.results.bindings[0];
		var uri_root = 'http://www.wikidata.org/entity/';
		mw.current_reference.P9073 = rrb.P9073 ? rrb.P9073.value.replace(uri_root,'') : '';
		mw.current_reference.P9073 = replaceAll(mw.current_reference.P9073, 'http://www.wikidata.org/entity/', '');
		// mw.current_reference.P1629 = rrb.P1629 ? rrb.P1629.value : '';
		// mw.current_reference.P1629 = replaceAll(mw.current_reference.P1629, 'http://www.wikidata.org/entity/', '');
		var rrb_p1629 = rrb.P1629 ? rrb.P1629.value : '';
		if ( mw.current_reference.P9073 == '') {
			$('.copyref-container .useasref_icon').css({'border':'', 'padding':''});
			var msg;
			if ( rrb_p1629 == '' ) {
				msg = 'Cannot use property ' +
					link_to_P(mw.current_reference.P) +
					' as a reference.<br>It lacks ' +
					link_to_P('P9073') +
					'. You may consider to add it.<br>' +
					'Please respect the constraints of P9073 and ask <a target="_blank" href="/w/index.php?title=Property_talk:P9073&action=edit&section=new">here</a> if you are unsure about the value of P9073 to be added.';
			}
			else {
				msg = 'Cannot use property ' +
					link_to_P(mw.current_reference.P) +
					' as a reference.<br>It has ' +
					link_to_P('P1629') +
					' but it lacks ' +
					link_to_P('P9073') +
					'. You may consider to add P9073 on the basis of P1629.<br>' +
					'Please respect the constraints of P9073 and ask <a target="_blank" href="/w/index.php?title=Property_talk:P9073&action=edit&section=new">here</a> if you are unsure about the value of P9073 to be added.';
			}
			$( '#useasref_dialog_msg' )
				.html(msg)
				.css('color','red');
			$( '#useasref_dialog' ).dialog( 'open' ); $('.useasref_icon').css('width','16px');
		}
		else {
			$('.pasteref-container').css('pointer-events',''); // enable paste buttons
			$('.pasteref-container a').css('opacity',''); // no opacity
			mw.notify( 'Reference copied: ' + mw.current_reference.P + ' ' + mw.current_reference.value, {
				title: 'UseAsRef - Info',
				type: 'info'
			});
		}
	}
	
	function copy_reference ( identifier, external_id, P1810 ) {
		// console.log('copy_reference',identifier.attr('id'), external_id); // test
		mw.current_reference = {};
		mw.current_reference.P = identifier.attr('id');
		mw.current_reference.value = external_id;
		mw.current_reference.P1810 = P1810;
		// P9073 must be a property of mw.current_reference.P; if present, P1629 may help to add P9073
		var sparql_query = 'select ?P9073 ?P1629 where \
			{ optional { wd:'+ mw.current_reference.P +' wdt:P9073 ?P9073 . } \
			  optional { wd:'+ mw.current_reference.P +' wdt:P1629 ?P1629 . } }';
		makeSPARQLQuery('https://query.wikidata.org/sparql', sparql_query, doneCallback);
		return mw.current_reference;
	}
	
	function addButton_paste( $identifier ) {
		var statements = $($identifier).find('.wikibase-snakview-body'); // qualifiers are statements too
		var statement = statements[0]; // add paste button only to the main statement, get rid of qualifiers
		$(statement).append(
			$( '<div>' )
			.attr( 'class', 'pasteref-container' )
			.css( {
				'float': 'right',
				'position': 'relative',
				'z-index': 1,
				'pointer-events':'none'
			} )
			.append(
				$( '<a>' )
				// .text('Paste Ref')
				.html('<img class="useasref_icon" src="'+icon_paste+'">')
				.css({'opacity':'0.4'})
				.attr( {
					'href': '#',
					'title': 'paste copied ref',
					'class': 'pasteref-button',
				} )
				.on( 'click', function ( event ) {
					event.preventDefault();
					paste_reference( event.target, mw.current_reference );
				} )
			)
		);
	}
	
	function addButton_copy( $identifier ) {
		var ids = $identifier.find('a.external');
		ids.after( // prepend o append non sembrano utili per la cattura del valore dell'ID
			$( '<div>' )
			.attr( 'class', 'copyref-container' )
			.css( {
				'float': 'right',
				'position': 'relative',
				'z-index': 1,
			} )
			.append(
				$( '<a>' )
				.html('<img class="useasref_icon" src="'+icon_copy+'">')
				.attr( {
					'href': '#',
					'title': 'copy this as ref',
					'class': 'copyref-button',
				} )
				.on( 'click', function ( event ) {
					event.preventDefault();
					var external_id = $(event.target).parents('.wikibase-snakview-value').eq(0).find('a').text();
					$('.copyref-container .useasref_icon').css({'border':'', 'padding':''});
					$('.pasteref-button img').css({'border':'', 'padding':''}); // sb, 2021-02-13
					$(event.target).css({'border':'3px solid green', 'padding':'2px'});
					var P1810_a = $(event.target).parents('.wikibase-statementview-mainsnak-container').find('a[title="Property:P1810"]');
					var P1810 = $(P1810_a) ? $(P1810_a).parents('div.wikibase-snakview').find('.wikibase-snakview-value').text() : '';
					mw.current_reference = copy_reference( $identifier, external_id, P1810 );
				} )
			)
		);
	}
	
	function replaceAll(s, s_search, s_replace) {
		return s.split(s_search).join(s_replace);
	}
	
	function build_up_dialog () {
		$( '<div>' )
			.attr( 'id', 'useasref_dialog' )
			.append(
				$( '<p>' )
				.attr( 'id', 'useasref_dialog_msg' )
				.html('qui messaggio')
			)
			.dialog( {
				dialogClass: 'useasref_dialog',
				title: 'Use As Ref' + ' <img class="useasref_icon" src="'+icon_copy+'">'+' <img class="useasref_icon" src="'+icon_paste+'">',
				autoOpen: false,
				modal: true,
				width: 500,
				buttons: [ {
					id: 'useasref_dialog-button-close',
					text: 'Close',
					click: function ( event ) {
						event.preventDefault();
						$( '#useasref_dialog' ).dialog( 'close' );
					}
				} ]
			} );
	}
	
 	function useasref_init () {
 		// console.log('init (re)start'); // test
		var ids = $('.wikibase-statementgrouplistview>div').get(2); // the block containing identifiers
		var div_ids = $(ids).find('>div'); // the divs of each identifier; inside each div, more identifiers can exist: .find('a.external')

		// wait for identifiers
		if ( div_ids.length === 0 ) {
			mw.useasref_retries++;
			if (mw.useasref_retries >= mw.useasref_max_retries) {
				return; // stop trying
			}
			setTimeout(function(){ useasref_init(); }, 200);
			return;
		}
		
		div_ids = div_ids
			.not('#P212, #P213, #P214, #P236, #P646, #P2671, #P791, #P957, #P2333, #P7859'); // exclude ID of some properties
			/* TODO better solution: use a select
			SELECT ?p WHERE {
				{ ?p p:P9073/a wdno:P9073 . } 
				UNION { ?p wdt:P9073 ?value . FILTER (isBlank(?value)) }
			}
			and add VIAF P214, ISNI P213, WorldCat P7859
			*/
		div_ids = div_ids.toArray();
		$( div_ids ).each( function () {
			addButton_copy( $( this ) );
		} );
		
		$( '.wikibase-statementview' ) // add paste button to statements
			.not($($('.wikibase-statementgrouplistview>div').get(2)).find('.wikibase-statementview')) // do not add paste button to identifiers
			.each ( function () {
				addButton_paste( $( this ) );
			} );
			
		build_up_dialog(); // prepare a dialog, now closed
		$( '#identifiers' ).after(' \
			<img class="useasref_info" src="'+icon_paste+'"> \
			<img class="useasref_info" src="'+icon_copy+'">');
		$( '.useasref_info' )
			.attr('title','UseAsRef Gadget - Info')
			.css({'position':'relative', 'float':'right', 'width':'16px', 'cursor':'pointer'})
			.click(function(){
				$('#useasref_dialog_msg').html('<div>UseAsRef version 1.0.1 (2021-02-13)<p><a target="_blank" href="/wiki/User:Bargioni/UseAsRef">More info</a></p></div>');
				$( '#useasref_dialog' ).dialog( 'open' );
			});
		$('.useasref_icon').css('width','16px');
		$('.useasref_icon').attr('unselectable', 'on').css('user-select', 'none').on('selectstart', false); // prevent selectable
 		console.log('UseAsRef loaded');
 	}
 	
 	// do not apply to items that are properties
 	if (document.location.href.indexOf('Property:P') > -1) return; // mw.config.values.wgTitle ?
 	
 	$.when(
		mw.loader.using( [
			'jquery.spinner', 'jquery.ui', 'mediawiki.api',
			'mediawiki.util', 'wikibase.api.RepoApi',
		] )
		.then( function () {
			mw.useasref_retries = 0; 
			mw.useasref_max_retries = 50;
			// icons
			icon_warning = 'https://upload.wikimedia.org/wikipedia/commons/d/d9/Warning_sign_font_awesome-red.svg';
			icon_copy = 'https://upload.wikimedia.org/wikipedia/commons/1/15/Font_Awesome_5_solid_copy.svg';
			icon_paste = 'https://upload.wikimedia.org/wikipedia/commons/4/4d/Font_Awesome_5_solid_file-import.svg';
			icon_ok = 'https://upload.wikimedia.org/wikipedia/commons/0/05/Font_Awesome_5_solid_check-circle.svg';
			icon_help = '?';
			// object to paste
			mw.current_reference = {};
			// required?
			// api = new mw.Api();
			// repoApi = new wb.api.RepoApi( api );
		} ),
		$.ready
	)
	.then( useasref_init );
 	
} ( mediaWiki, wikibase, jQuery ) );
});