User:Tpt/viaf.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.
( function ( mw, $ ) {
	'use strict';


	//i18n
	var i18nData = {
		'en': {
			'addViaf-box-title': 'Add identifiers from VIAF',
			'addViaf-box-title-id-label': 'VIAF identifier',
			'addViaf-done': 'Importation done',
			'addViaf-error-api': 'Error in the API',
			'addViaf-import': 'Import',
			'addViaf-nothingtodo': 'All the identifiers have already been imported.',
			'addViaf-searchonviaf': 'Search on VIAF.'
		},
		'fr': {
			'addViaf-box-title': 'Ajouter des identifiants depuis VIAF',
			'addViaf-box-title-id-label': 'identifiant VIAF',
			'addViaf-done': 'L\'importation a été faite',
			'addViaf-error-api': 'Erreur dans l\'API',
			'addViaf-import': 'Importer',
			'addViaf-nothingtodo': 'Tout les identifiants ont déjà été importés.',
			'addViaf-searchonviaf': 'Rechercher dans VIAF.'
		}
	};

	var language = mw.config.get( 'wgUserLanguage' );
	var parts = language.split("-");
	var lang = 'en';
	/*if( parts[1]=='hans' || parts[1]=='cn' || parts[1]=='sg' || parts[1]=='my' ) {
		var lang = 'zh-hans';
	} else if ( parts[1]=='hant' || parts[1]=='hk' || parts[1]=='tw' || parts[1]=='mo' ) {
		var lang = 'zh-hant';
	} else*/ if ( i18nData[parts[0]] ) {
		lang = parts[0];
	}
	mw.messages.set( $.extend( i18nData.en, i18nData[lang] ) );


	var notification = mw.util.getParamValue( 'addviafnotif' );
	if ( notification !== null ) {
		var notifications = notification.split('|');
		for( var i in notifications ) {
			mw.notify( mw.msg( notifications[i] ) );
		}
	}

	var itemId = mw.config.get( 'wbEntityId' );
	if( !itemId ) {
		return;
	}

	//TODO: update
	var propertyForType = {
		RSL:{key:'RSL',p:947} ,
		NSZL:{key:'NSZL',p:951} ,
		BNE:{key:'BNE',p:950} ,
//TODO: bad format		BNC:{key:'BNC',p:1273} , /* BNC = CANTIC */
		BIBSYS:{key:'BIBSYS',p:1015} , /* "Norway (BIBSYS)" ; [[viaf:106122570]] */
		BAV:{key:'BAV',p:8034} , /* "Vatican Library" ; WMF: 'la' */
		EGAXA:{key:'EGAXA',p:1309} , /* "Bibliotheca Alexandrina (Egypt)" ; [[viaf:102319859]] */
		xA:{key:'xA',p:0} ,
		VIAF:{key:'VIAF',p:214} ,
		DNB:{key:'GND',p:227} ,
		GND:{key:'GND',p:227} ,
		PND:{key:'GND',p:227} ,
		LC:{key:'LCCN',p:244} ,
		LCCN:{key:'LCCN',p:244} ,
		SUDOC:{key:'SUDOC',p:269} ,
		NDL:{key:'NDL',p:349} ,
		//NUKAT:{key:'NUKAT',p:1207} , Buggy, seems to provide something like an internal VIAF id
		CINII:{key:'CINII',p:271} ,
		ISNI:{key:'ISNI',p:213} ,
		JPG:{key:'ULAN',p:245} ,
		ULAN:{key:'ULAN',p:245} ,
		SELIBR:{key:'LIBRIS',p:906} ,
		LIBRIS:{key:'LIBRIS',p:906} ,
		NLA:{key:'NLA',p:409} ,
		NKC:{key:'NKC',p:691} ,
		ICCU:{key:'ICCU',p:396} ,
		BNF:{key:'BNF',p:268} ,
		BPN:{key:'BPN',p:651} ,
//		NLI:{key:'NLI',p:949} , /* National Library of Israel */ 
		NLR:{key:'NLR',p:1003} , /* "National Library of Romania */
		PTBNP:{key:'PTBNP',p:1005} , /* "Biblioteca Nacional de Portugal" */
		NTA:{key:'NTA',p:1006} , /* "National Library of the Netherlands" */
		ORCID:{key:'ORCID',p:496},
		B2Q:{key:'B2Q',p:3280},
		LAC:{key:'LAC',p:1670},
		RERO:{key:'RERO',p:3065}
	};

	/**
	 * Create the dialog and add a link in toolBox
	 */
	function init() {
		mw.loader.using( ['jquery.ui'], function() {
			mw.hook( 'wikibase.entityPage.entityLoaded' ).add( function( entity ) {
				var searchKey = '';
				if( entity.labels && entity.labels.en ) {
					searchKey = entity.labels.en.value;
				}
				var formFind = '<div id="addViaf" title="' + mw.msg( 'addViaf-box-title' ) + '"><form id="addViaf-form">';
				formFind += '<p><label for="addViaf-updateLabels">' + mw.msg( 'addViaf-box-title-id-label' ) + '</label> <input type="number" required name="addViaf-id" id="addViaf-id" /></p>';
				formFind += '<p><a href="http://viaf.org/viaf/search?query=' + mw.util.rawurlencode( 'local.names all "' + searchKey + '"' ) + '&sortKeys=holdingscount">' + mw.msg( 'addViaf-searchonviaf' ) + '</a></p>';
				formFind += '</form></div>';
				var messageFind = $( formFind ).appendTo( '#content' );
				messageFind.dialog( {
					autoOpen: false,
					modal: true,
					width: 500,
					buttons: [ {
						id: 'addViaf-button-import',
						text: mw.msg( 'addViaf-import' ),
						click: work
					} ]
				} );
				$( '#addViaf-form' ).submit( function( event ) {
					event.preventDefault();
					work();
				} );

				var portletLink = mw.util.addPortletLink( 'p-tb', '#', mw.msg( 'addViaf-box-title' ), 't-addViaf', mw.msg( 'addViaf-box-title' ) );
				$( portletLink ).click( show );
			} );
		} );
	}

	/**
	 * Show the dialog
	 */
	function show() {
		var id = $( '#P214 .wikibase-statementview-mainsnak .wikibase-snakview-variation-valuesnak' ).first().text();
		$( '#addViaf-id' ).val( id );
		$( '#addViaf' ).dialog( 'open' );
		return false;
	}

	/**
	 * Called on error
	 */
	function onError() {
		$.removeSpinner( 'addViaf' );
		$( '#addViaf-button-import' ).button( 'option', 'disabled', false );
		$( '#addViaf' ).dialog( 'close' );
	}

	/**
	 * Called on API error
	 */
	function onApiError( jqXHR, textStatus, errorThrown ) {
		mw.log.error( 'viaf.js API error: ' + JSON.stringify( [ jqXHR, textStatus, errorThrown ] ) );
		alert( mw.msg( 'addViaf-error-api' ) );
		onError();
	}

	/**
	 * Update interLanguages links from the wiki selected in the box. Works only for Wikipedias.
	 */
	function work() {
		mw.loader.using( ['jquery.spinner', 'mediawiki.api'], function() {
			$.createSpinner( {
				size: 'large',
				type: 'block',
				id: 'addViaf'
			} ).appendTo( 'addViaf' );
			$( '#addViaf-button-import' ).button( 'option', 'disabled', true );

			var id = $( '#addViaf-id' ).val();
			var api = new mw.Api();

			getItem( api, function( item ) { //get a refreshed version of the item
				getViafIds( id, function( ids ) {
					var ids2 = [];
					var additionalReferences = [];
					$.each( ids, function(i, id) {
						var existingStatement = getStatement( item, id.property, id.value );
						if( !existingStatement ) {
							ids2.push( id );
						} else if( ( !existingStatement.references || existingStatement.references.length === 0 ) && existingStatement.id ) {
							//add reference
							additionalReferences.push( existingStatement.id );
						}
					} );
					//TODO: preview
					if( ids2.length === 0 && additionalReferences.length === 0 ) {
						mw.notify( mw.msg( 'addViaf-nothingtodo' ) );
						onError();
					} else {
						addIdentifiers( api, ids2, item.lastrevid, function( baserevid ) {
							// TODO: debug
							// addAdditionalReferences( api, additionalReferences, baserevid, function( baserevid ) {
								location.href = mw.util.getUrl( null, { addviafnotif: 'addViaf-done' } );
							//} );
						} );
					}
				} );
			} );
		} );
	}

	/**
	 * Return the statement with the given property value
	 * @param item array the item
	 * @param property string the property id
	 * @param value string the value
	 * @return {object|null} the statement or null if not found
	 */
	function getStatement( item, property, value ) {
		if( !item.claims || !item.claims[property] ) {
			return null;
		}
		for( var i in item.claims[property] ) {
			if( item.claims[property][i].mainsnak.datavalue.value == value ) {
				return item.claims[property][i];
			}
		}
		return null;
	}

	/**
	 * Update the item
	 * @param identifiers list of identifiers to add
	 */
	function addIdentifiers( api, identifiers, baserevid, success ) {
		if( identifiers.length === 0 ) {
			return success( baserevid );
		}
		var identifier = identifiers.pop();
		api.postWithEditToken( {
			action : 'wbcreateclaim',
			entity : itemId,
			snaktype : 'value',
			property : identifier.property,
			value : JSON.stringify(identifier.value),
			baserevid : baserevid,
		} )
		.done(function( data ) {
			if( data.pageinfo && data.pageinfo.lastrevid && data.claim && data.claim.id ) {
				setImportedFrom( api, data.claim.id, 54919, data.pageinfo.lastrevid, function( baserevid ) {
					addIdentifiers( api, identifiers, baserevid, success );
				} );
			} else {
				onApiError( data );
			}
		} )
		.fail(onApiError);
	}

    /**
	 * Update the item
	 * @param statements the statements to add
	 */
	function addAdditionalReferences( api, statementIds, baserevid, success ) {
		if( statementIds.length === 0 ) {
			success( baserevid );
		}
		var id = statementIds.pop();
		setImportedFrom( api, id, 54919, baserevid, function( baserevid ) {
			addAdditionalReferences( api, statementIds, baserevid, success );
		} );
	}

	/**
	 * Add a reference "imported from"
	 * @param statementId string the statement id
	 * @param from integer the source page identifier (without "Q")
	 * @param baserevid integer the last revision id
	 * @param success function called on success
	 */
	function setImportedFrom( api, statementId, from, baserevid, success ) {
		api.postWithEditToken( {
			action: 'wbsetreference',
			statement: statementId,
			snaks: JSON.stringify( { 'P248': [ {
				snaktype: 'value',
				property: 'P248',
				datavalue: {
					value: {
						'entity-type': 'item',
						'numeric-id': from
					},
					type: 'wikibase-entityid'
				}
			} ] } ),
			baserevid : baserevid
		} )
		.done(function( data ) {
			if( data.pageinfo && data.pageinfo.lastrevid ) {
				success( data.pageinfo.lastrevid );
			} else {
				onApiError( data );
			}
		} )
		.fail(onApiError);
	}

	/**
	 * Return the item
	 * @param success function called on success
	 */
	function getItem( api, success ) {
		api.get( {
			'action': 'wbgetentities',
			'ids': itemId
		} )
		.done(function( data ) {
			if( data.success && data.entities[itemId] ) {
				success( data.entities[itemId] );
			} else {
				onApiError( data );
			}
		} )
		.fail(onApiError);
	}


	/**
	 * Return the ids for a VIAF entry
	 * @param id string the VIAF id
	 * @param success function called on success
	 */
	function getViafIds( id, success ) {
		$.getJSON( '//viaf.org/viaf/' + id + '/justlinks.json' ).done(function( data ) {
			var ids = [];
			$.each( data, function( type, temp ) {
				if( type === 'viafID' ) {
					ids.push( {
						property: 'P' + propertyForType.VIAF.p,
						type: 'VIAF',
						value: temp
					} );
				} else if( type in propertyForType ) {
					$.each( temp, function( k, id ) {
						var cleanedId = cleanId( type, id );
						var prop = propertyForType[type].p;
						if( prop === 906 && isNaN( cleanId ) ) {
							prop = 5587; // It's a LIBRIS ID and not a SELIBR one
						}
						if( cleanedId !== null ) {
							ids.push( {
								property: 'P' + prop,
								type: type,
								value: cleanedId
							} );
						}
					} );
				}
			} );
			success( ids );
		} )
		.fail(onApiError);
	}

	/**
	 * Clean an id
	 * @param type string the id type
	 * @param id string the id
	 * @return string|null the cleaned id or null if there is an error
	 */
	function cleanId( type, id ) {
		switch( type ) {
			case 'LC': //Bug: no literal prefix in some justlinks.json. Why?????
				if( /^[a-z]+\d{8,10}$/.exec( id ) ) {
					return id;
				}
				return null;
			case 'DNB':
				var result = /^http:\/\/d-nb\.info\/gnd\/([\dxX]+)$/.exec( id );
				if( result ) {
					return result[1].toUpperCase();
				}
				return null;
			case 'NLA':
				return id.replace( /^0+/, '' );
			case 'BNC':
				return id.replace( /^\./, '' );
			case 'BNF':
				var result =  /ark:\/12148\/cb([a-z0-9]+)$/.exec( id );
				if( result ) {
					return result[1];
				}
				return null;
			case 'ICCU':
				return id.replace( 'IT\\ICCU\\', '' ).replace( '\\', '' );
			case 'ISNI':
				var result =  /^(\d{4})(\d{4})(\d{4})([\dxX]{4})$/.exec( id );
				if( result ) {
					return result[1] + result[2] + result[3] + result[4].toUpperCase();
				}
				return null;
			case 'EGAXA':
				return id.replace( /^vtls/, '');
			case 'ORCID':
				var result = /^http:\/\/orcid.org\/([\dxX-]+)$/.exec( id );
				if( result ) {
					return result[1].toUpperCase();
				}
				return null;
			case 'NLR':
				var result = /^\d+$/.exec( id );
				if( result ) {
					return result;
				}
				return null;
			case 'RERO':
				return '02-' + id;
			default:
				return id;
		}
	}

	$( document ).ready( init );
} ( mediaWiki, jQuery ) );