User:Tambuccoriel/authority-control.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.
// <nowiki>
var wd_authority_control = {

	q_prefix:'Q',
	p_prefix:'P',
	birth_year:'',
	death_year:'',

	id : 'authority_control_results' ,
	api : '/w/api.php' ,
	allowedCodes : {
		WKP:{key:'WKP',p:0} ,
		NSZL:{key:'NSZL',p:951} ,
		BNE:{key:'BNE',p:950} ,
		BNC:{key:'BNC',p:9984} , /* 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} , /* "NUKAT Center (Poland)" ; Ludwik Lejzer Zamenhof ([[Q11758]]) [[viaf:73885295]] */
		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:7029} , /* "National Library of Russia */
		PTBNP:{key:'PTBNP',p:1005} , /* "Biblioteca Nacional de Portugal" */
		NTA:{key:'NTA',p:1006} , /* "National Library of the Netherlands" */
		N6I:{key:'N6I',p:1946} , /* National Library of Ireland */
		LAC:{key:'LAC',p:1670} , /* Library of Canada */
		ORCID:{key:'ORCID',p:496}
	} ,

	searching_viaf:false,

	init : function () {
		var self = this ;

		var portletLink = mw.util.addPortletLink( 'p-tb', '#', 'Authority Control','t-wd_ac');
		$(portletLink).click ( function () {
			self.run() ;
			return false ;
		} ) ;

	} ,
	
	run : function () {
		var self = this ;
		//self.name = $( '.wb-firstHeading' ).find( '.wikibase-labelview-text' ).text();
		self.name = $('h1 span.wikibase-title-label').text().replace(/\([PQ]\d+\)$/,'') ;
		$('#'+self.id).remove() ;
		$('#mw-content-text').before ( "<div id='"+self.id+"' title='Authority control' style='overflow:auto'><div id='"+self.id+"_content'></div></div>" ) ;
		$('#'+self.id).dialog ( {
			modal : true ,
			height:400 ,
			maxWidth: 1000,
			width : 'auto'
		} ) ;
//		$($('div.wb-claims-section').get(0)).before ( "<div style='padding:2px;margin:2px;border:5px solid #DDDDDD;position:relative' id='"+self.id+"'></div>" ) ;
		self.results = [] ;
		$.each ( ['en','de','fr','ja','es','commons'] , function ( k , v ) { self.lookOnWP ( v ) ; } ) ;

		var dates ;
		self.birth_year = $($('#P569 .wikibase-snakview-variation-valuesnak').get(0)).text().replace(/^.*?(\d{3,4}).*$/,'$1') ;
		self.death_year = $($('#P570 .wikibase-snakview-variation-valuesnak').get(0)).text().replace(/^.*?(\d{3,4}).*$/,'$1') ;
		if ( self.birth_year+self.death_year!='' ) dates = self.birth_year + ' ' + self.death_year ;
		self.lookOnVIAF ( self.name , dates ) ;
	} ,
	isWebKit : function () {
		return (navigator.userAgent.match(/WebKit/)==null)?false:true;
	} ,
	getNodeName : function ( name ) { // Node name; there is a bug with Chrome, but recent jQuery update removed $.browser
		var self = this ;
		return "ns" + viaf_record_counter + "\\:"+name ;
	} ,


	gotJSON : function ( data ) {
	//	console.log ( data ) ;
	} ,

	lookOnVIAF : function ( name , dates ) {
		var self = this ;
		if ( !self.searching_viaf ) {
			self.viaf_num = self.results.length ;
			self.results.push ( { source:'viaf.org' , status:'searching' , data:[] } ) ;
			self.updateResults(1) ;
			self.searching_viaf = true ;
		}
		
		var r = Math.random() ;

		var search_key = name ;
		if ( typeof dates != 'undefined' ) search_key += ' ' + dates ;

		$.ajax ( {
			dataType: "json",
			url : '//tools.wmflabs.org/magnustools/authority_control.php?callback=?' ,
			data : { query : 'viaf' , key : search_key , r:Math.random() } ,
			timeout : 15000 ,
			error : function () {
				alert ( "VIAF or WMF Labs have a problem; no VIAF data at this time. Maybe later.") ;
			} ,
			success : function ( d ) {
			var xml = $.parseXML ( d.result ) ;
			vd = $(xml) ;
viaf_record_counter=1;
			vd.find('record').each ( function ( id , record ) {
				r = $(record) ;
viaf_record_counter++ ;
//				if ( !$.browser.chrome ) console.log ( r )
				var data = { ids : [] , comment : [] } ;
				
				data.birth_date = $(r.find(self.getNodeName('birthDate'))).text() ;
				data.death_date = $(r.find(self.getNodeName('deathDate'))).text() ;

				var viaf = r.find(self.getNodeName('viafID')).text() ;
				data.ids.push ( { code:'VIAF' , value:viaf } ) ;
				data.page = "http://viaf.org/viaf/" + viaf + "/" ;

				r.find(self.getNodeName('source')).each ( function ( k , v ) {
					if ( ($(v).attr('differentiated')||'').toLowerCase() == 'false' ) return ;
					var n = $(v).text().split('|') ;
					if ( n.length != 2 ) return ;
					if ( undefined === self.allowedCodes[n[0].toUpperCase()] ) return ;
					var thecode = self.allowedCodes[n[0].toUpperCase()].key ;
					data.ids.push ( { code:thecode , value:self.fixCodeValue(thecode,n[1]) } ) ;
//					data.ids[n[0]] = n[1] ;
				} ) ;

				r.find(self.getNodeName('mainHeadings')).each ( function ( k , v ) {
					$($(v).find(self.getNodeName('text'))).each ( function ( k2 , v2 ) {
						data.comment.unshift ( $(v2).text() ) ;
					} ) ;
				} ) ;
				r.find(self.getNodeName('titles')).each ( function ( k , v ) {
					$($(v).find(self.getNodeName('text'))).each ( function ( k2 , v2 ) {
						data.comment.push ( $(v2).text() ) ;
					} ) ;
				} ) ;
				
				data.comment = "<div>" + data.comment.join('</div><div>') + "</div>" ;
				var result = self.results[self.viaf_num];
				if(!result) return;
				result.data.push ( data ) ;
				
				if ( result.length >= 10 ) return false ;
				
			} ) ;
			
			if ( self.results[self.viaf_num].data.length == 0 ) {
				if ( typeof dates != 'undefined' ) {
					return self.lookOnVIAF ( name ) ; // Try again, without dates
				}
				self.results[self.viaf_num].status = 'no results' ;
			} else self.results[self.viaf_num].status = 'done' ;
			self.updateResults(-1) ;

		} } ) ;
	} ,
	
	lookOnWP : function ( lang ) {
		var self = this ;
		var rk = self.results.length ;
		var project = lang=='commons'?'wikimedia':'wikipedia' ;
		self.results.push ( { source:(lang=='commons'?lang:lang+'.'+project) , status:'searching' , data:[] } ) ;
		self.updateResults() ;
		var article ;
		if ( lang == 'commons' ) {
			article = 'Creator:'+self.name ;
		} else {
			var lookfor = 'https://' + lang + '.'+project+'.org/wiki/' ;
			$('div.wikibase-sitelinkgrouplistview a').each ( function () {
				var href = $(this).attr('href') ;
				if ( undefined === href || href.substr(0,lookfor.length) != lookfor ) return ;
				article = $(this).text() ;//href.substr(lookfor.length) ;
				return false ;
			} ) ;
		}
		
		if ( undefined === article ) {
			self.results[rk].status = 'no article' ;
			self.updateResults() ;
			return ;
		}

		self.running++ ;
		$.getJSON ( "//" + lang + "."+project+".org/w/api.php?callback=?" , {
			action : 'query' ,
			prop : 'revisions' ,
			titles : article ,
			rvprop : 'content|timestamp' ,
			format : 'json'
		} , function ( d ) {
			$.each ( d.query.pages , function ( k , v ) {
				if ( v.revisions === undefined ) {
					self.results[rk].status = 'no article' ;
					self.updateResults(-1) ;
					return ;
				}
				var t = v.revisions[0]['*'].replace ( /\s+/g , ' ' ) ;
				
				if ( self.birth_year+self.death_year == '' ) {
					var m ;
					m=t.match(/:(\d+)[_ ]births\s*\]\]/); if(m!=null)self.birth_year=m[1] ;
					m=t.match(/:(\d+)[_ ]deaths\s*\]\]/); if(m!=null)self.death_year=m[1] ;

					m=t.match(/:[Gg]eboren[_ ](\d+)\s*\]\]/); if(m!=null)self.birth_year=m[1] ;
					m=t.match(/:[Gg]estorben[_ ](\d+)\s*\]\]/); if(m!=null)self.death_year=m[1] ;

					m=t.match(/:[Nn]aissance[_ ]en[_ ](\d+)\s*\]\]/); if(m!=null)self.birth_year=m[1] ;
					m=t.match(/:[Dd]écès[_ ]en[_ ](\d+)\s*\]\]/); if(m!=null)self.death_year=m[1] ;

					m=t.match(/:[Nn]acidos[_ ]en[_ ](\d+)\s*\]\]/); if(m!=null)self.birth_year=m[1] ;
					m=t.match(/:[Ff]allecidos[_ ]en[_ ](\d+)\s*\]\]/); if(m!=null)self.death_year=m[1] ;

					m=t.match(/\{\{NF\|(\d+)\|(\d+)/); if(m!=null){self.birth_year=m[1] ; self.death_year=m[2] ;}
				}
					
				

				var st = null ;
				if ( lang == 'en' || lang == 'commons' ) st = t.match ( /{{Authority[ _]control(.+?)}}/i ) ;
				else if ( lang == 'de' ) st = t.match ( /{{Normdaten(.+?)}}/i ) ;
				else if ( lang == 'ja' ) st = t.match ( /{{Normdaten(.+?)}}/i ) ;
				else if ( lang == 'fr' ) st = t.match ( /{{Autorité\s*\|(.+?)}}/i ) ;
				if ( st != null ) {
					var kv = {} ;
					var data = [] ;
					$.each ( st[1].split ( '|' ) , function ( dummy , kvp ) {
						var m = kvp.match ( /^\s*([a-z]+)\s*=\s*(.+?)\s*$/i ) ;
						if ( m == null ) return ;
						var code = $.trim(m[1]).toUpperCase() ;
						if ( undefined === self.allowedCodes[code] ) return ;
						var value = self.fixCodeValue ( code , m[2] ) ;
						data.push ( { code:code , value:value } ) ;
					} ) ;
					self.results[rk].data.push ( { ids:data , comment:"From authority control template" } ) ;
				}
				if ( self.results[rk].data.length == 0 ) self.results[rk].status = 'no results' ;
				else self.results[rk].status = 'done' ;
				self.updateResults(-1) ;
			
			} ) ;
		} ) ;
			
	} ,
	
	fixCodeValue : function ( code , value ) {
		value = $.trim ( value ) ;
		value = value.replace(/\s/g,'');
		
		if ( code == 'LCCN' ) {
			var m = value.match ( /^(.+)\/(\d+)\/(\d+)/ ) ;
			if ( m != null ) {
				while ( m[3].length < 6 ) m[3] = '0' + m[3] ;
				value = m[1] + '/' + m[2] + '/' + m[3] ;
			}
			value = value.replace(/\//g,'') ;
		}
		if ( code == 'NLR' ) {
			value = value.replace ( /^RUNLRAUTH/i , '' ) ;
		}
		if ( code == 'BAV' ) {
			value = value.replace ( /_/ , '/' ) ;
		}
		if ( code == 'BNF' ) {
			value = 'cb' + value.replace ( /\D/g , '' ) ; // Digits only
			var bnf_xdigits = '0123456789bcdfghjkmnpqrstvwxz'; // A few lines from https://en.wikisource.org/wiki/User:Inductiveload/BnF_ARK_format
			var bnf_check_digit = 0;
			
			for (var i=0; i < value.length; i++){
				bnf_check_digit += bnf_xdigits.indexOf(value[i]) * (i+1);
			}
			value = value.substr(2) + bnf_xdigits[bnf_check_digit % bnf_xdigits.length]; //29 is the radix
		}
		if ( code == 'EGAXA' ) {
			value = value.replace ( /^vtls/i , '' ) ;
		}
		if ( code == 'NLA' ) {
			value = value.replace ( /^0+/i , '' ) ;
		}
		return value ;
	} ,
	
	updateResults : function ( d ) {
		var self = this ;
		if ( d !== undefined ) self.running += d ;
		var h = '' ;
		if ( self.birth_year !== '' || self.death_year !== '' ) {
			h += "<div style='font:14pt'>" + self.name + " (" + (self.birth_year||'?') + " &ndash; " + (self.death_year||'?') + ")</div>" ;
		}
		
		h += "<table border=1 cellspacing=0 cellpadding=2>" ;
		h += "<tr><th>Source</th><th colspan=4>Result</th></tr>" ;
		$.each ( self.results , function ( k , v ) {
			var rows = 0 ;
			$.each ( v.data , function ( k2 , v2 ) { rows += v2.ids.length } ) ;
			if ( rows == 0 ) rows = 1 ;
			h += "<tr>" ;
			h += "<td nowrap valign='top' rowspan=" + rows + ">" + v.source + "</td>" ;
			if ( v.data.length == 0 ) {
				if ( v.status == 'searching' ) h += "<td colspan=4 style='color:#2DC800'><i>" + v.status + "</i></td>" ;
				else h += "<td colspan=4><i>" + v.status + "</i></td>" ;
			} else {
				$.each ( v.data , function ( k1 , v1 ) {
					if ( k1 != 0 ) h += "</tr><tr>" ;
					$.each ( v1.ids , function ( k2 , v2 ) {
						if ( k2 > 0 ) h += "</tr><tr>" ;
						else {
							h += "<td valign='top' rowspan=" + v1.ids.length + "><a href='#' title='Add these AC data to the item' onclick='wd_authority_control.addSet("+k+","+k1+");return false'>(+)</a>" ;
							if ( v1.page !== undefined ) h += "<br/><a title='Show original data in new tab' target='_blank' href='" + v1.page + "' style='font-size:14pt'>&rArr;</a>" ;
							h += "</td>" ;
						}
						h += "<td>" + v2.code + "</td>" ;
						
						h += "<td nowrap>" ;
						var p = self.allowedCodes[v2.code].p ;
						var exp = $('div.wb-claim-section-P'+p+' div.valueview-value') ;
						var add_item_link = "&nbsp;<a href='#' title='Add this to the item' onclick='wd_authority_control.addSet("+k+","+k1+","+k2+");return false'>(+)</a>" ;
						if ( exp.length == 0 ) h += v2.value + add_item_link ;
						else {
							var identical = false ;
							exp.each ( function ( ek , ev ) {
								var t = $(ev).text().replace(/\s/g,'') ;
								if ( t == '' ) t = $($(ev).find('textarea')).val().replace(/\s/g,'') ;
								if ( $.trim(t.replace(/[ \/]/g,'')) == $.trim(v2.value.replace(/[ \/]/g,'')) ) identical = true ;
							} ) ;
							var col = identical ? 'blue' : 'red' ;
							h += "<span style='color:" + col + "'>" + v2.value + "</span>" ;
							if ( !identical ) {
								if ( p == 0 ) h += "&nbsp;<span title='This property is not yet available on Wikidata, but might soon be!'>&times;</span>" ;
								else h += add_item_link ;
							}
						}
						h += "</td>" ;
						
						
						if ( k2 == 0 ) {
							h += "<td valign='top' rowspan="+v1.ids.length+"><div style='max-height:100px;overflow:auto;font-size:8pt;max-width:600px'>" ;
							if ( v1.birth_date !== undefined || v1.death_date !== undefined ) {
								var hd = "<div>" + (v1.birth_date||'?') + " &ndash; " + (v1.death_date||'?') + "</div>" ;
								if ( self.birth_year !== '' ) hd = hd.replace ( self.birth_year , '<b>'+self.birth_year+'</b>' ) ;
								if ( self.death_year !== '' ) hd = hd.replace ( self.death_year , '<b>'+self.death_year+'</b>' ) ;
								h += hd ;
							}
							h += v1.comment ;
							h += "</div></td>" ;
						}
					} ) ;
				} ) ;
			}
			h += "</tr>" ;
		} ) ;
		h += "</table>" ;
		h += "<div><b>Legend:</b>&nbsp;<span style='color:blue'>Already in item</span>&nbsp;<span style='color:red'>Contradicts item</span></div>" ;
		$('#'+self.id+'_content').html ( h ) ;
		$('#'+self.id).dialog ( { position : { my: "center", at: "center", of: window } } ) ;
		$('#'+self.id+' a').css ( { color : 'blue' } ) ;
	} ,
	
	addSet : function ( k1 , k2 , k3 ) {
		var self = this ;
		self.claims2add = [] ;
		if ( k3 === undefined ) {
			$.each ( self.results[k1].data[k2].ids , function ( k3 , v3 ) {
				var p = self.allowedCodes[v3.code].p ;
				self.claims2add.push ( [ p , v3.value ] ) ;
			} ) ;
		} else {
			var v3 = self.results[k1].data[k2].ids[k3] ;
			var p = self.allowedCodes[v3.code].p ;
			self.claims2add.push ( [ p , v3.value ] ) ;
		}
		$('#'+self.id).css ( { 'background-color' : '#444444' } ) ;
		self.setNextClaim() ;
	} ,
	
	setNextClaim : function () {
		var self = this ;
		if ( self.claims2add.length == 0 ) {
			$('#'+self.id).css ( { 'background-color' : '' } ) ;
			return ;
		}
		var x = self.claims2add.shift() ;
		if ( x[0] == 0 ) self.setNextClaim() ; // Future property
		else self.tryCreateClaim ( x[0] , x[1] ) ;
	} ,

	tryCreateClaim : function ( property , value ) {
		var self = this ;
		var entity = mw.config.get('wgTitle') ;
		property += '' ;
		value += '' ;
		entity = self.q_prefix + entity.replace ( /\D/g , '' ) ;
		property = property.replace ( /\D/g , '' ) ;
		$.getJSON ( self.api , {
			action : 'wbgetentities' ,
			format : 'json' ,
			ids : entity ,
			props : 'info|claims'
		} , function ( data ) {
			if ( undefined !== data.entities[entity] ) {
				if ( undefined !== data.entities[entity].claims ) {
					if ( undefined !== data.entities[entity].claims[self.p_prefix+property] ) {
						var n = data.entities[entity].claims[self.p_prefix+property] ;
						var exists = false ;
						$.each ( n , function ( k , v ) {
							if ( v.mainsnak.datavalue.value == value ) {
								exists = true ;
								return false ;
							}
						} ) ;
						if ( exists ) {
							console.log ( "p"+property+' exists for '+entity ) ;
							self.setNextClaim() ;
							return ;
						}
					}
				}
			}
//			console.log ( "Claiming " + property + " : " + value ) ;
			self.createClaim ( entity , property , value ) ;
		} ) ;
	} ,
	
	
	createClaim : function ( entity , property , value ) {
//		console.log ( "Creating " + entity + " / " + property + " / " + value ) ;
		var self = this ;
		
		property = property.replace(/\D/g,'') ;
		entity = entity.replace(/\D/g,'') ;
		
		$.get ( self.api , {
			action : 'query' ,
			prop : 'info' ,
			titles : self.q_prefix+entity ,
			format : 'json'
		} , function ( data ) {
			var lastrevid ;
			$.each ( (data.query.pages||[]) , function ( k , v ) {
				lastrevid = v.lastrevid ;
			} ) ;
//			var vo = JSON.parse ( value ) ;
//			var value_id = vo['numeric-id']+'' ;
			
			$.post ( self.api , {
				action : 'wbcreateclaim' ,
				entity : self.q_prefix+entity ,
				snaktype : 'value' ,
				property : self.p_prefix+property ,
				value : JSON.stringify ( value ) ,
				token : mw.user.tokens.get( 'csrfToken' ) ,
				baserevid : lastrevid ,
				format : 'json' ,
				summary : 'using [[User:Magnus Manske/authority control.js|authority control.js]]'
			} , function ( data ) {
				var id = 'added_' + self.id_cnt ;
				var h = "<div class='wb-claim-section-P"+property.replace(/\D/g,'')+"'>Added AC data : <div style='display:inline-block' class='valueview-value'>"+value+"</div></div>" ;
				$($('div.wb-claims').get(0)).append ( h ) ;
				self.updateResults() ;
				self.setNextClaim() ;
			} , 'json' ) ;
			
		} , 'json' ) ;
	
	} ,
	fin : true
} ;

$( function () {
	if ( mw.config.get('wgNamespaceNumber') != 0 ) return ;
	if ( mw.config.get('wgAction') != 'view' ) return ;

	mw.loader.using( [ 'jquery.ui', 'user.options' ] ).then( function () {
		wd_authority_control.init () ;
	} ) ;
} ) ;
// </nowiki>