User:Bargioni/quickNames.js

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.
/**
 * Gadget to help add given and family names to items with names in Latin chars
 * described in User:Bargioni/QuickNames
 * By User:Epìdosis, User:Bargioni
 * March 26, 2022 - April 6, 2022
 * P9139 added April 25, 2022
 * P2358, P2359, P2365 added May 9, 2022
 * "given name with this initial" added May 13, 2022
 * P6978 added August 25, 2022
   ___        _      _    _   _                              _     
  / _ \ _   _(_) ___| | _| \ | | __ _ _ __ ___   ___  ___   (_)___
 | | | | | | | |/ __| |/ /  \| |/ _` | '_ ` _ \ / _ \/ __|  | / __|
 | |_| | |_| | | (__|   <| |\  | (_| | | | | | |  __/\__ \_ | \__ \
  \__\_\\__,_|_|\___|_|\_\_| \_|\__,_|_| |_| |_|\___||___(_)/ |___/
                                                          |__/
 * https://patorjk.com/software/taag/#p=display&f=Standard&t=QuickNames                                                          
 */

/*jshint esversion: 6 */

mw.loader.using([ 'jquery.ui' ]).then(function () {

$( function($) { // our workspace

	function makeSPARQLQuery( endpointUrl, sparqlQuery, doneCallback ) {
		var settings = {
			headers: { Accept: 'application/sparql-results+json' },
			data: { query: sparqlQuery }
		};
		return $.ajax( endpointUrl, settings ).then( doneCallback );
	}

	function build_link(t, tooltip) {
		// Qnnn becomes clickable
		let qt = $(t).text();
		$(t).html(`<a title="${tooltip}" href="https://www.wikidata.org/wiki/${qt}" target="_blank">${qt}</a>`);
	}
	
	function QN_test_P1545(P) {
		/*
		P esiste nell'item, controllo già fatto in precedenza
		per ogni 
		mw.QN.item.entities[mw.QN.qid].claims[P] con length > 1 
		deve esistere un .qualifiers.P1545;
		Se è così, return true
		*/
		let claims = mw.QN.item.entities[mw.QN.qid].claims[P];
		if (claims.length > 1) {
			for (let i=0; i < claims.length; i++) {
				let claim = claims[i];
				if (!claim.qualifiers || !claim.qualifiers.P1545) return false;
			}
		}
		return true; // ci sono tutti i P1545 o non sono necessari
	}
	
	function QN_renum() {
		var button_clicked = this;
		var P = $(button_clicked).attr('id').replace(/renum_/,'');
		console.log('apply P1545 to '+P); // test
		mw.QN.renum_occ = 1; // counter
		// get the latest version of the item
		let url = 'https://www.wikidata.org/wiki/Special:EntityData/'+mw.QN.qid+'.json';
		$.getJSON(url, function(R) {
			mw.QN.item = R;
			QN_renumber(P);
		});
	}

	function QN_renumber(P) {
		let icon_ok   = '<img width="32px" src="https://upload.wikimedia.org/wikipedia/commons/b/b7/Gtk-ok.svg">';
		let icon_ko   = '<img width="32px" src="https://upload.wikimedia.org/wikipedia/commons/3/37/Emblem-important-red.svg">';
		let icon_gear = '<img width="32px" src="https://upload.wikimedia.org/wikipedia/commons/c/cd/Vector_Loading_fallback.gif">';
		// disable renum buttons while running
		$('.button_renum').button( "option", "disabled", true );
		$('.button_renum').css('opacity','0.5');
		$('#renum_msg_'+P).html(icon_gear);
		let occs = mw.QN.item.entities[mw.QN.qid].claims[P]; // occurrences of P
		mw.QN.renum_occs = occs.length;
		mw.QN.renum_P = P;
		let C = occs[mw.QN.renum_occ - 1]; // current occ of P
		// existing qualifiers will be removed! Is this a problem?
		C["qualifiers-order"] = [ "P1545" ];
		C.qualifiers = {
			"P1545" : [
				{
					"property"  : "P1545",
					"snaktype"  : "value",
					"datavalue" : {
						"type"  : "string",
						"value" : mw.QN.renum_occ+'' // series ordinal as string
					}
				 }
			]
		};
		let api = new mw.Api();
		let token = mw.user.tokens.values.csrfToken;
		api.post({
			// add P1545 to current occ of P
			'action'     : 'wbsetclaim',
			'claim'      : JSON.stringify(C),
			'format'     : 'json',
			'token'      : token,
			'summary'    : "Added P1545 with [[User:Bargioni/QuickNames|QuickNames]]"	
		})
		.then( function(aw) { 
			// console.log(aw);
			mw.QN.renum_occ++;
			if (mw.QN.renum_occ <= mw.QN.renum_occs) QN_renumber(mw.QN.renum_P); // add P1545 to next occ of P
			else {
				$('#renum_msg_'+mw.QN.renum_P).html(icon_ok);
				// enable renum buttons back again
				let claims = mw.QN.item.entities[mw.QN.qid].claims;
				mw.QN.occ_P734 = claims.P734 ? claims.P734.length : 0;
				mw.QN.occ_P735 = claims.P735 ? claims.P735.length : 0;
				setup_renum_button('P735', mw.QN.occ_P735);
				setup_renum_button('P734', mw.QN.occ_P734);
				mw.notify(`Added ${mw.QN.renum_occs} series ordinals (P1545) to ${mw.QN.renum_P} `, {title: 'QuickNames', type: 'info'});
			}
		})
		.fail( function(e) { 
			console.log('error',e);
			$('#renum_msg_'+mw.QN.renum_P).html(icon_ko);
			mw.notify(`An error occurred while adding ${mw.QN.renum_occs} series ordinals (P1545) to ${mw.QN.renum_P} `, {title: 'QuickNames', type: 'error'});
		 });
	}
	
	function setup_renum_button(P, occ) {
		if (occ > 1) {
			$('#renum_'+P).button( "option", "disabled", false ).css('opacity','1.0');
		}
		else {
			$('#renum_'+P).button( "option", "disabled", false ).css('opacity','0.5');
		}
		$('#renum_'+P).attr('title',`${occ} occurrences of ${P}`);
	}

	function QN_exists(row, item_Q) {
		// check if the item_Q found by QN_search is used in the item
		let f = 0;
		let P = row.find('.word_type_menu').val();
		if (!mw.QN.item.entities[mw.QN.qid].claims[P]) return 0;
		for (var i = 0; i < mw.QN.item.entities[mw.QN.qid].claims[P].length; i++) {
			let vv = mw.QN.item.entities[mw.QN.qid].claims[P][i];
			if (vv.mainsnak.datavalue.value.id == item_Q) f = 1;
		}
		return f;
	}
	
	sort_by_description_desc = function(a,b){
		if (a.description == b.description) return 0; 
		if (a.description >  b.description) return -1; 
		if (a.description <  b.description) return 1;
	};

	function QN_search(name, row) {
		let url = 'https://www.wikidata.org/w/api.php?action=wbsearchentities&format=json&errorformat=plaintext&language=en&uselang=en&type=item&limit=25&search=';
		url += encodeURIComponent(name);
		$.getJSON(url, function(R) {
			let word_type = row.find('.word_type_menu').find('option:selected').attr('data-type');
			// console.log('search', name, row, word_type, R); // test
			R.search = R.search.sort( sort_by_description_desc ); // reason of sort: process male g.n. before female g.n. to ensure indexOf
			let item_Q = '?'; let item_description = '?'; let item_unisex;
			for (let i = 0; i < R.search.length; i++) {
				let name = R.search[i];
				name.description = name.description ? name.description.replace(/surname/,'family name') : ''; // equivalence
				// if (mw.QN.gender_code == 'f') name.description = name.description ? name.description.replace(/unisex given name/,'female given name') : ''; // equivalence
				// if (mw.QN.gender_code == 'm') name.description = name.description ? name.description.replace(/unisex given name/,'male given name') : ''; // equivalence
				if (name.description.indexOf('unisex given name') > -1) item_unisex = name.id;
				if (name.description.indexOf("given name") > -1 && name.description.indexOf("surname") > -1) continue; // do not accept wrong items with "both given name" and "surname" in description
				// given name with this initial. Full name undetermined, not generally used or not determinable - 2022-05-13
				if (name.description.indexOf('given name with this initial') == 0 && (word_type == 'male given name' || word_type == 'female given name')) {
					console.log('given name with this initial', name.id, word_type); // test
					item_Q = name.id;
					item_description = 'given name with this initial';
				} 
				if (name.description.indexOf(word_type) > -1 && name.match.text == R.searchinfo.search && XRegExp('^[\\p{Latin}\\p{Common}]+$').test(name.description)) {
					item_Q = name.id;
					item_description = name.description;
					break;
				}
			}
			if (item_Q == '?' && item_unisex) {
				// we didn't find a fe/male name, we found a unisex name (e.g. Dominique)
				item_Q = item_unisex;
				if (mw.QN.gender_code == 'f') item_description = 'female given name';
				if (mw.QN.gender_code == 'm') item_description = 'male given name';
			}
			row.find('.qn_item').text(item_Q);
			if (item_Q != '?') {
				// console.log(row.find('.word_type_menu').val()); // test
				if (row.find('.word_type_menu').val() == 'P734' && get_property_value('P734')) row.find('.button_add').css('opacity','0.5').prop('disabled',true);
				if (row.find('.word_type_menu').val() == 'P735' && get_property_value('P735')) row.find('.button_add').css('opacity','0.5').prop('disabled',true);
				row.find('.button_create').css('opacity','0.5').prop('disabled',true);
				if (!QN_exists(row,item_Q)) row.find('.button_add').css('opacity','1.0').prop('disabled',false);
				build_link( row.find('.qn_item'), item_description );
			}
			else {
				row.find('.button_add').css('opacity','0.5').prop('disabled',true);
				row.find('.button_create').css('opacity','1.0').prop('disabled',false);
			}
		});
	}
	
	function get_property(P) {
		let item = mw.QN.item.entities[qid];
		let claims = item.claims[P] ? item.claims[P] : [];
		return claims;
	}

	function get_property_value(P) {
		// return the value of the first claim
		let v = get_property(P).length ? get_property(P)[0].mainsnak.datavalue : null;
		if (v) {
			if (v.type == 'time') { return v.value.time }
			if (v.type == 'wikibase-entityid') { return v.datavalue ? v.datavalue.value.id : v.value.id }
			if (v.type == 'string') { return v.datavalue ? v.datavalue.value : v.value }
			alert('QuickNames: unknown type '+v.type);
			console.error('QuickNames: unknown type '+v.type);
		}
	}

	function QN_check_item() {
		let instance = get_property_value('P31');
		if (instance != 'Q5') {
			console.log('QuickNames: P31 is not Q5, exit');
			// mw.notify('P31 of this item is not Q5, exit', {title: 'QuickNames', type: 'warn'});
			return false;
		}
		let qid = mw.QN.qid;
		if (get_property('P2358').length || get_property('P2359').length || get_property('P2365').length) {
			console.log(`QuickNames: not applicable to items with P2358, P2359 or P2365, exit`); // Roman name
			mw.notify(`Not applicable to items with P2358, P2359 or P2365, exit`, {title: 'QuickNames', type: 'warn'});
			return false;
		}
		let label = mw.QN.item.entities[qid].labels[mw.QN.user_lang];
		if (!label) {
			console.log(`QuickNames: no ${mw.QN.user_lang} label, exit`);
			mw.notify(`No ${mw.QN.user_lang} label, exit`, {title: 'QuickNames', type: 'warn'});
			return false;
		}
		else {
			mw.QN.label = label.value;
		}
		let gender = get_property_value('P21');
		let genders = ['Q6581097', 'Q6581072']; // male (Q6581097) or female (Q6581072)
		if (genders.indexOf(gender) == -1) {
			console.log(`QuickNames: no P21 Q6581097 or Q6581072 in this item, exit`);
			mw.notify(`No P21 Q6581097 or Q6581072 in this item, exit`, {title: 'QuickNames', type: 'warn'});
			return false;
		}
		mw.QN.gender_code = 'm';
		if (gender == 'Q6581072') mw.QN.gender_code = 'f';
		if (get_property('P735').length && get_property('P734').length) {
			if ( QN_test_P1545('P734') && QN_test_P1545('P735') ) {
				console.log(`QuickNames: this item has both P734 and P735, exit`);
				mw.notify(`This item has both P734 and P735, exit`, {title: 'QuickNames', type: 'info'});
				return false;
			}
			else {
				console.log(`QuickNames: this item has both P734 and P735, but lacks P1545`);
				mw.notify(`This item has both P734 and P735, but lacks P1545`, {title: 'QuickNames', type: 'info'});
				return true;
			}
		}
		return true;
	}

	function QN_box_builder_refresh() {
		$('#QuickNames_tabs').remove();
		QN_box_builder();
	}
	
	function QN_box_builder() {
		let html = `
<div id="QuickNames_tabs">
	<div id="QuickNames_close">
		<img style="height:28px" src="https://upload.wikimedia.org/wikipedia/commons/1/16/469-elephant.svg">
		<span id="QuickNames_name">QuickNames 2</span> <a href="#" title="Close this box" onclick="$(&quot;#QuickNames_tabs&quot;).remove(); return false">×</a>
		<span id="QN_refresh" title="Reload this box" style="cursor:pointer">&#8635;</span>
	</div>
	<ul>
		<li><a href="#QuickNames_editor"><span>Editor</span></a></li>
		<li class="test"><a href="#QuickNames_settings"><span>Settings</span> <span id="QN_lang_code_in_tab" title="current language code for new names"></span></a></li>
		<li><a href="#QuickNames_info"><span>&#9432; Info</span></a></li>
	</ul>
	
	<div id="QuickNames_editor">
		<table id="QuickNames_editor_table">
		</table>
		<table id="QuickNames_table_renum"><tr>
			<td><input type="button" id="renum_P735" class="button_qn button_renum" value="Apply series ordinal (P1545) to given names"/></td>
			<td class="renum_msg" id="renum_msg_P735" width="60px"></td>
			<td><input type="button" id="renum_P734" class="button_qn button_renum" value="Apply series ordinal (P1545) to family names"/></td>
			<td class="renum_msg" id="renum_msg_P734" width="60px"></td>
		</tr></table>
	</div>

	<div class="test" id="QuickNames_settings">
		<table>
			<tr><td>Language code of new names</td><td><input size="4" id="QN_settings_lang" value=""> <span id="QN_language_name"></span></td></tr>
			<tr><td>Split label also by char</td><td><input size="4" id="QN_settings_splitter" value=""></td></tr>
		</table>
		<p>Settings are saved in a cookie.</p>
	</div>
	
	<div id="QuickNames_info">
		<img src="https://upload.wikimedia.org/wikipedia/commons/9/96/Idea.svg" width="24px"/>
		See <a href="/wiki/User:Bargioni/QuickNames" target="_blank">QuickNames help page</a> for more info on this gadget and how to use it.
		<br/>Version 2.3 - August 25, 2022
	</div>
</div>
`;
		$('.wikibase-entitytermsview-entitytermsforlanguagelistview').append(html);
		let Cs = mw.cookie.get('QN_settings') || '{}';
		let C = JSON.parse(Cs);
		// $('#QN_settings_lang').val( C.lang || wb.getUserLanguages()[0] );
		$('#QN_settings_lang').val( C.lang || 'mul' ); // ASAP will be forced to 'mul', based on https://www.wikidata.org/wiki/Wikidata:WikiProject_Names#Basic_principles
		$('#QN_settings_splitter').val( C.splitter || '' );
		$('#QN_language_name').text( wb.getLanguageNameByCode(C.lang) );
		if ( !mw.cookie.get('QN_settings') ) QN_save_cookie();
		$('#QN_lang_code_in_tab').text(`[${C.lang}]`);
		$('#QN_settings_lang').change( QN_save_cookie );
		$('#QN_settings_splitter').change( QN_save_cookie );
		$('#QuickNames_tabs').tabs();
		
		// P1545 buttons
		let claims = mw.QN.item.entities[mw.QN.qid].claims;
		mw.QN.occ_P734 = claims.P734 ? claims.P734.length : 0;
		mw.QN.occ_P735 = claims.P735 ? claims.P735.length : 0;
		$('.button_renum').button(); // style renum buttons
		setup_renum_button('P735', mw.QN.occ_P735);
		setup_renum_button('P734', mw.QN.occ_P734);
		$('.button_renum').click(QN_renum); // non agisce se restano disabled
		style_the_box();

		let gender = get_property_value('P21');
		gender = gender == 'Q6581072' ? 'female' : 'male';
		setup_input_fields(gender);

		$('#QN_refresh').on('click',function(){ QN_box_builder_refresh(); });
	}
	
	function QN_save_cookie() {
		let v = $('#QN_settings_lang').val().trim() || wb.getUserLanguages()[0];
		$('#QN_settings_lang').val(v);
		$('#QN_lang_code_in_tab').text(`[${v}]`); // show current lang code
		v = $('#QN_settings_splitter').val().trim();
		$('#QN_settings_splitter').val(v);
		let C = {};
		C.lang = $('#QN_settings_lang').val().trim();
		// check validity of C.lang code using wb.getLanguageNameByCode
		if (wb.getLanguageNameByCode(C.lang) == C.lang && C.lang != 'mul') {
			mw.notify('Invalid language code, cookie was not saved', {title: 'QuickNames', type: 'error'});
			$('#QN_language_name').text('???');
			return false;
		}
		$('#QN_language_name').text( wb.getLanguageNameByCode(C.lang) );
		// show the lang code outside the settings tab? where? TODO
		C.splitter = $('#QN_settings_splitter').val().trim();
		mw.cookie.set('QN_settings', JSON.stringify( C ));
		mw.notify('Cookie saved', {title: 'QuickNames', type: 'success'});
	}

	function style_the_box() {
		// style some components of the QuickNames
		$('#QuickNames_close').css({'float':'right', 'font-size':'12pt', 'position':'relative', 'left':'-10px', 'top':'6px'});
		$('#QuickNames_name').css({'font-size':'11.2pt', 'padding-right':'25px'});
		$('#QuickNames_close').css({'color':'rgb(39, 121, 170)'});
		$('#QuickNames_tabs').css({'font-size':'1em', 'margin-top':'5px', 'margin-bottom':'5px'});
		$('#QuickNames_tabs ul span').not('#QN_lang_code_in_tab').css('font-weight', 'bold');
		$('#QuickNames_editor_table').css('border','1px solid black');
		$('#QuickNames_settings table, #QuickNames_settings table td').css('border','1px solid black');
	}

	function setup_input_fields(gender) {
		let label = mw.QN.label;
		label = label.replace(/,/g,''); // get rid of ","
		let splitter = ' ';
		splitter += $('#QN_settings_splitter').val() ? ('|'+$('#QN_settings_splitter').val()) : '';
		let re = new RegExp(splitter);
		let words = label.trim().replace(/\s+/g, ' ').split( re );
		let word_type = select_word_type('given name', gender);
		let buttons = quickNames_buttons(word_type);
		words.forEach(word => {
			$('#QuickNames_editor_table').append(`<tr><td>${word_type}</td><td class="qn_word">${word}</td><td class="qn_item"></td><td>${buttons}</td></tr>`);
			word_type = select_word_type('family name', gender);
		});
		$('#QuickNames_editor_table td').css('border','1px solid gray');
		$('.button_qn').button(); // style buttons
		// disable join buttons
		$('.button_join').eq(0).prop('disabled',true).css('opacity','0.5'); // disable first join button
		if ($('.button_join').length == 2) $('.button_join').eq(1).prop('disabled',true).css('opacity','0.5'); // two names? disable second join button
		// disable buttons family name if P734 is in item
		if (get_property_value('P734')) {
			disable_button_add('family name');
			disable_button_create('family name');
		}
		// disable buttons given name if P735 is in item
		if (get_property_value('P735')) {
			disable_button_add(gender + ' given name');
			disable_button_create(gender + ' given name');
		}
		// on load
		$('.qn_word').each(function(i, word) {
			QN_search($(word).text(), $(word).closest('tr'));
		});
		// change events
		$('.word_type_menu').on('change', function(e){ QN_word_type_change(e.target) });
		// click events
		$('.button_join').on('click',function(e){ QN_join(e.target); });
		$('.button_add').on('click',function(e){ QN_add(e.target); });
		$('.button_create').on('click',function(e){ QN_create(e.target); });
	}
	
	function disable_button_add(word_type) {
		let buttons = $('.button_add');
		buttons.each(function(i,button){
			let row = $(button).closest('tr');
			let wt = $(row).find('.word_type_menu option:selected').attr('data-type');
			if (wt == word_type) $(button).css('opacity','0.5').prop('disabled',true);
		});
	}

	function disable_button_create(word_type) {
		let buttons = $('.button_create');
		buttons.each(function(i,button){
			let row = $(button).closest('tr');
			let wt = $(row).find('.word_type_menu option:selected').attr('data-type');
			if (wt == word_type) $(button).css('opacity','0.5').prop('disabled',true);
		});
	}

	function QN_word_type_change (select_menu) {
		let row = $(select_menu).closest('tr');
		$(row).find('.qn_item').text('...');
		let word = $(row).find('.qn_word').text();
		QN_search(word, row);
	}
	
	function get_item_type_by_prop(p) {
		let type = 'fn'; // valid for P734, P9139, P1950, P6978
		if (p == 'P735') type = mw.QN.gender_code + 'gn';
		return type;
	}
	
	function QN_create (clicked_button) {
		let name_language = $('#QN_settings_lang').val();
		let row = $(clicked_button).closest('tr');
		let property = $(row).find('.word_type_menu').val(); // P734 fn, P735 gn, P1950 2nd fn, P6978 2nd fn, P9139 1st fn
		let word = $(row).find('.qn_word').text();
		let item = QN_setup_item( get_item_type_by_prop(property), word, name_language );
		save_new_name(item, row); // will also add claim to item
	}
	
	function QN_join(clicked_button) {
		let row = $(clicked_button).closest('tr');
		let previous_row = row.prev();
		let word = $(row).find('.qn_word').text();
		let previous_word = $(previous_row).find('.qn_word').text();
		$(previous_row).find('.qn_word').text(previous_word+' '+word);
		// update qn_item
	    QN_search(previous_word+' '+word, previous_row);
		// slowly remove row
		row.find('td').fadeOut(700, 
			function() {
				$(this).parents('tr:first').remove();
			});
	}
	
	function quickNames_buttons(word_type) {
		// word_type nec.?
		let s = [];
		s.push('<input type="button" class="button_join button_qn" value="join with previous"/>');
		s.push('<input type="button" class="button_add button_qn" value="add claim"/>');
		s.push('<input type="button" class="button_create button_qn" value="create and add"/>');
		return s.join(' ');
	}
	
	function select_word_type(word_type, gender) {
		let s = [];
		s.push('<select style="height:32px" class="word_type_menu">');
		let sel = ''; if (word_type == 'given name') sel = 'selected="selected"';
		s.push(`<option value="P735" data-type="${gender} given name" ${sel}>given name (P735)</option>`);
		sel = ''; if (word_type == 'family name') sel = 'selected="selected"';
		s.push(`<option value="P734" data-type="family name" ${sel}>family name (P734)</option>`);
		s.push(`<option value="P1950" data-type="family name">2nd family name (Spanish) (P1950)</option>`);
		s.push(`<option value="P6978" data-type="family name">middle family name (Scandinavian) (P6978)</option>`);
		s.push(`<option value="P9139" data-type="family name">1st family name (Portugal) (P9139)</option>`);
		s.push('</select>');
		return s.join('');
	}
	
	function get_item( qid ) {
		let url = 'https://www.wikidata.org/wiki/Special:EntityData/'+qid+'.json';
		$.getJSON(url, function(R) {
			mw.QN.item = R;
			if ( QN_check_item() ) QN_box_builder();
		});
	}
	
	function QN_setup_claim(P, id) {
		let item_id = mw.QN.qid.toLowerCase(); // qnnn
		let claim = {
			"id": (new wb.utilities.ClaimGuidGenerator(item_id)).newGuid(),
			"type": "claim",
			"mainsnak": {
				"snaktype": "value",
				"property": P,
				"datavalue": {
					"value": {
						"entity-type": "item",
						"numeric-id": id
					},
					"type": "wikibase-entityid"
				},
				"datatype": "wikibase-item"
			}
		}
		return claim;
	}
	
	function save_new_claim(claim, row) {
		// console.log('I am going to save claim', claim, row); // test
		let api = new mw.Api();
		let token = mw.user.tokens.values.csrfToken;
		api.post({
			'action': 'wbsetclaim',
			'format': 'json',
			'token': token,
			'summary': "Added with [[User:Bargioni/QuickNames|QuickNames]]",
			'claim': JSON.stringify(claim)
		}).then(function(aw){
			// console.log(aw); // aw.success=1, 
			let P = aw.claim.mainsnak.property;
			mw.notify(`${P} successfully added. Please, reload the page to see the changes`, {title: 'QuickNames', type: 'info'});
			row.find('.button_add').css('opacity','0.5').prop('disabled',true);
			// refresh of P1545 buttons
			if (P == 'P734') mw.QN.occ_P734++;
			if (P == 'P735') mw.QN.occ_P735++;
			setup_renum_button('P735', mw.QN.occ_P735);
			setup_renum_button('P734', mw.QN.occ_P734);
		}).catch(function(aw) {
			console.log('QuickNames error',aw);
			mw.notify("An error occurred while adding the name", {title: 'QuickNames', type: 'error'});
		});
	}
	
	function QN_add(clicked_button) {
		let row = $(clicked_button).closest('tr');
		let P = $(row).find('.word_type_menu').val(); // property of the new claim
		let Qid = $(row).find('.qn_item').text(); // Q of the name
		let id = Qid.replace(/^Q/,''); // id of the name
		// console.log('I am going to save claim', P, id); // test
		let claim = QN_setup_claim(P, id);
		save_new_claim(claim, row);
	}
	
	function QN_setup_item(type, value, lang) {
		// create a new item 
		// allowed values for type: fn, mgn, fgn
		// value is the f/mg/fg name
		// lang must be a valid Wikimedia language code, see https://www.wikidata.org/wiki/Help:Wikimedia_language_codes/lists/all
		
		let item = {
			"claims": {
				"P31": [{
					"mainsnak": {
						"snaktype": "value",
						"property": "P31",
						"datavalue": {
							"value": {
								"entity-type": "item",
								"numeric-id": '???', // use 101352 for fn, 12308941 for mgn, 11879590 for fgn
								"id": "Q???" // use Q101352 for fn, Q12308941 for mgn, Q11879590 for fgn
							},
							"type": "wikibase-entityid"
						},
						"datatype": "wikibase-item"
					},
					"type": "statement",
					"rank": "normal"
				}],
				"P282": [{
					"mainsnak": {
						"snaktype": "value",
						"property": "P282", // alfabeto
						"datavalue": {
							"value": {
								"entity-type": "item",
								"numeric-id": 8229,
								"id": "Q8229" // latin alphabet
							},
							"type": "wikibase-entityid"
						},
						"datatype": "wikibase-item"
					},
					"type": "statement",
					"rank": "normal"
				}],
				"P1705": [{ // native label
					"mainsnak": {
						"snaktype": "value",
						"property": "P1705",
						"datavalue": {
							"value": {
								"text": "???", // the value
								// "language": lang
								"language": 'mul'
							},
							"type": "monolingualtext"
						},
						"datatype": "monolingualtext"
					},
					"type": "statement",
					"rank": "normal"
				}]
			},
			"labels": {
				"it": {
					"language": "it",
					"value": "???" // the value
				},
				"en": {
					"language": "en",
					"value": "???" // the value
				}
			},
			"descriptions": {
				"it": {
					"language": "it",
					"value": "???" // use cognome for fn, prenome maschile for mgn, prenome femminile for fgn
				},
				"en": {
					"language": "en",
					"value": "???" // use family name for fn, male given name for mgn, female given name for fgn
					// also for the second Spanish family name
				}
			},
			// "aliases": {}
		}
	
		let numeric_id;
		if (type == 'fn')  numeric_id = 101352;
		if (type == 'mgn') numeric_id = 12308941;
		if (type == 'fgn') numeric_id = 11879590;
		item.claims.P31[0].mainsnak.datavalue.value['numeric-id'] = numeric_id;
		item.claims.P31[0].mainsnak.datavalue.value.id = 'Q' + numeric_id;
	
		item.claims.P1705[0].mainsnak.datavalue.value.text = value;
		item.labels.it.value = value;
		item.labels.en.value = value;
		// if (['it','en'].indexOf(lang) == -1) {
		if (['it','en'].indexOf(lang) == -1 && lang != 'mul') {
			// add label in lang language
			item.labels[lang] = {};
			item.labels[lang].language = lang;
			item.labels[lang].value = value;
		}
		
		let desc;
		if (type == 'fn')  desc = 'cognome';
		if (type == 'mgn') desc = 'prenome maschile';
		if (type == 'fgn') desc = 'prenome femminile';
		item.descriptions.it.value = desc;
		if (type == 'fn')  desc = 'family name';
		if (type == 'mgn') desc = 'male given name';
		if (type == 'fgn') desc = 'female given name';
		item.descriptions.en.value = desc;
		
		return item;
	}
	
	function save_new_name(item, row) {
		let api = new mw.Api();
		let token = mw.user.tokens.values.csrfToken;
		api.post({ 
			'action': 'wbeditentity',
			'new': 'item',
			'summary': "Created with [[User:Bargioni/QuickNames|QuickNames]]",
			'data': JSON.stringify(item),
			'token': token
		}).then(function(aw){
			// console.log(aw); // aw.success=1, aw.entity.id is the new Q
			mw.notify(`Name successfully created ${aw.entity.id}`, {title: 'QuickNames', type: 'info'});
			$(row).find('.qn_item').text(aw.entity.id);
			build_link( $(row).find('.qn_item') );
			row.find('.button_create').css('opacity','0.5').prop('disabled',true);
			QN_add( $(row).find('.button_add') );
		}).catch(function(aw) {
			console.log('QuickNames error',aw);
			mw.notify("An error occurred while creating the name", {title: 'QuickNames', type: 'error'});
		});
	}

	// check if we're viewing an item
	var qid = mw.config.get( 'wbEntityId' );
	if ( !qid ) return;
	// 
	console.log('QuickNames gadget loaded');
	$('head').append('<script src="https://unpkg.com/xregexp/xregexp-all.js"></script>'); // load xregexp library
	mw.QN = {};
	mw.QN.qid = qid;
	mw.QN.user_lang = wb.getUserLanguages()[0];
	get_item(qid);

}); // end of our workspace

}); // end of mw.loader.using