User:Magnus Manske/author strings.js
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)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
// <nowiki>
/*
To add this gadget, put the following line on your common.js User subpage:
importScript( 'User:Magnus_Manske/author_strings.js' );
*/
let author_strings = function () {
mw.loader.using(['vue', '@wikimedia/codex', 'jquery.ui', 'mediawiki.api'], function ( require ) {
// Code to run on page load
if ( mw.config.get('wgNamespaceNumber') != 0 ) return ;
if ( mw.config.get('wgAction') != 'view' ) return ;
if ( $('#P2093').length==0 // author name string, on papers
&& $('#P496,#P1153,#P1960,#P2798,#P106 a[title="Q1650915"]').length==0 // author
&& $('#P31 a[title="Q5"]').length==0 // human (portlet link)
) return ;
const CdxButton = require( '@wikimedia/codex' ).CdxButton;
const CdxCheckbox = require( '@wikimedia/codex' ).CdxCheckbox;
const CdxTextInput = require( '@wikimedia/codex' ).CdxTextInput;
const AuthorStringsApp = {
template: `<div>
<div style='float: right; font-size: 8pt;'>
<a href='/wiki/User:Magnus_Manske/Author_strings' target='_blank'>
<img src='https://upload.wikimedia.org/wikipedia/commons/thumb/6/6d/Breathe-help-browser.svg/16px-Breathe-help-browser.svg.png' border=0 />
</a>
</div>
<div v-if='name_collisions.length>0' style='border-bottom:1px dotted #DDD; margin-bottom: 1rem;'>
<h3>Author/author name string duplicates</h3>
<div style='display: flex;'>
<table>
<thead>
<tr>
<th nowrap>Author #</th>
<th>Author</th>
<th>Author string name</th>
</tr>
</thead>
<tbody>
<tr v-for='nc in name_collisions'>
<td>{{nc.serial}}</td>
<td>
<div v-for='entry in nc.p50'>
<a :href='"/wiki/"+entry.q' target='_blank'>{{entry.name}}</a>
</div>
</td>
<td>
<div v-for='entry in nc.ans'>
<div style='display: flex;'>
<CdxCheckbox v-model='entry.checked'></CdxCheckbox>
{{entry.name}}
</div>
</div>
</td>
</tr>
</tbody></table>
<div style='vertical-align: top;'>
<cdx-button @click.prevent='removeANSStatements' aria-label='Remove selected statements'>Remove selected statements</cdx-button><br/>
Make sure the authors are the same in each case!
</div>
</div>
</div>
<div v-if='loading'><i>Loading...</i></div>
<div v-else-if='papers.length==0'><i>No candidates.</i></div>
<table v-else-if='show="authors"' class='author_strings_authors' style='border-collapse: collapse;'>
<tbody>
<tr v-for='author_key in authors_order'>
<td style='vertical-align: top;'>
<table>
<tr v-for='o in authors[author_key].occurrences'>
<td style='width: 1rem;'>
<CdxCheckbox v-if='typeof o.q=="undefined"' v-model='o.checked'></CdxCheckbox>
<span v-else-if='typeof o.status!="undefined" && o.status=="done"' style='color: green;'>✓</span>
</td>
<td nowrap>
<span v-if='typeof o.q=="undefined"'>{{o.name}}</span>
<a v-else :href='"/wiki/"+o.q' target='_blank'>{{o.name}}</a>
</td>
<td>
<a :href='"/wiki/"+o.paper_q' target='_blank'>
<small>{{paper_name[o.paper_q]}}</small>
</a>
<div v-if='typeof o.status!="undefined" && o.status!="done"' style='color: red;'><small>{{o.status}}</small></div>
</td>
</tr></table>
</td>
<td style='vertical-align: top; width: 15rem; min-width: 15rem;'>
<p style='text-align:center;'>
<a @click.prevent='onToggle(author_key,"on")'>All</a> |
<a @click.prevent='onToggle(author_key,"toggle")'>Toggle</a> |
<a @click.prevent='onToggle(author_key,"off")'>None</a>
</p>
<p>
<a @click.prevent='searchForAuthor(author_key)' :title='author_key'>Search for author</a>
<span v-if='typeof author_search_results[author_key]!="undefined"'>
<span v-if='author_search_results[author_key].length==0' style='color:red'>
[none found]
</span>
<span v-else-if='author_search_results[author_key].length==1' style='color:green'>
[<a :href='"/wiki/"+author_search_results[author_key][0]' target='_blank'>
one found
</a>]
</span>
<span v-else-if='author_search_results[author_key].length>=10'>
[{{author_search_results[author_key].length}}+ found]
</span>
<span v-else>
[{{author_search_results[author_key].length}} found]
</span>
</span>
</p>
<p>
<a @click.prevent='createAndLinkAuthor(author_key)'>Create new author</a>
for the selected entries, and change the string author properties for authors (P50).
</p>
<p>
<form style='display: flex;'>
<cdx-text-input v-model="authors[author_key].tmp_q" placeholder='Qxxx' />
<cdx-button :disabled="(authors[author_key].tmp_q||'')==''?'true':null" :action="(authors[author_key].tmp_q||'')==''?'':'progressive'" @click.prevent='assignQtoAuthor(author_key)' aria-label='Assign author item ID'>Assign</cdx-button>
</form>
</p>
</td>
</tr>
</tbody>
</table>
<table v-else-if='show="papers"'>
<tr v-for='paper in papers'>
<td>
<a :href='"/wiki/"+paper.q' target='_blank'>
{{paper.label}}
</a>
</td>
<td>{{paper.count}}</td>
</tr>
</table>
</div>`,
data() { return {
loading:true,
name_collisions:[],
max_authors_for_permutations:4,
paper_count:{} ,
papers:[],
paper_name:{},
authors:{},
authors_order:[],
author_search_results:{},
show:'authors'
} } ,
created : function () {
mw.util.addCSS( 'table.author_strings_authors > tbody > tr { border-top: 2px solid #DDD; padding-top: 0.5rem;}' );
let self = this ;
let string_names = [] ;
if ( $('#P2093').length > 0 ) { // Paper
$('#P2093 div.wikibase-statementview-mainsnak div.wikibase-snakview-value').each ( function ( dummy , name ) {
string_names.push($(name).text());
} ) ;
string_names = string_names
.map(self.simplifyName)
.filter(function(name){return name!=''}) ;
string_names = self.uArray(string_names);
// Keep the first three authors, and the last one, because, you know...
while ( string_names.length > self.max_authors_for_permutations ) string_names.splice(self.max_authors_for_permutations-1,1) ;
self.permuteNamesAndQueryPapers(string_names,self.onPaperIDsLoaded) ;
self.checkDoubleAuthors();
} else if ( $('#P50').length == 0 ) { // Author
let name = self.simplifyName($('span.wikibase-title-label').text()).toLowerCase();
if ( name == '' ) return ; // Nothing useful to search for
let query = name + ' haswbstatement:P31=Q13442814' ;
self.searchForPaper ( query , function () {
self.onPaperIDsLoaded(function(){
Object.keys(self.authors).forEach((author_key)=>{
if ( name == author_key ) self.authors[author_key].tmp_q = wdutil.q ;
});
});
}) ;
}
} ,
methods: {
removeANSStatements : function () {
let self = this ;
let statement_ids = [] ;
self.name_collisions.forEach((x)=>{x.ans.filter((y)=>y.checked).forEach((y)=>{
statement_ids.push({id:y.statement_id,remove:''});
y.checked = false ;
})});
let data = {claims:statement_ids};
let payload = {
action: 'wbeditentity',
id: wdutil.q,
data: JSON.stringify(data) ,
summary: "Removing duplicate P2093s in favour of P50s (via author_strings gadget)"
} ;
let api = new mw.Api();
api.postWithToken("csrf", payload ).done(function( new_entity ) {
if ( new_entity.success==1 ) { // Update cache
wdutil.entity_cache[wdutil.q] = new_entity.entity ;
}
statement_ids.forEach((x)=>{$('#'+$.escapeSelector(x.id)).remove()});
self.checkDoubleAuthors();
} ).fail( function(code, new_entity) {
console.log(code,new_entity);
alert("Something went wrong with the API");
} );
} ,
changeAutorToP50Here : function(o,q) {
let self = this;
let param = {
paper_q:mw.config.get('wgTitle'),
name:o.name,
status:'',
q:q
};
if ( $("#P50").length == 0 ) wdutil.createPropertyContainerElement("P50") ;
wdutil.loadEntities(["P50"],function(){
self.changeOccurrenceStringNameToP50(param,function(){
// Create fake P50 statement in interface
if ( $('#'+q+q).length==0 ) {
let value_html = wdutil.getValueHTML("P50",q);
value_html = value_html.replace(/>[^<]+<\/a>/,'>'+o.name+'</a> ['+o.serial+']');
wdutil.createStatementElement("P50",value_html,q+q,q+q);
$('[id="'+o.statement_id+'"]').remove();
}
});
})
},
addAuthorNameLinks: function(o) {
let self = this;
let div = document.createElement('span');
div.style='font-size:8pt;';
let a = document.createElement('a');
a.style='margin-right:0.2rem;';
a.innerHTML='💡';
a.href='#';
a.title='Create a new author and assign this one';
a.onclick = function(){
// let name = self.simplifyName(o.name);
let aliases = [];
// if ( name!=o.name ) aliases.push(o.name);
aliases = aliases.map((name) => { return {language:'en',value:name} } );
let payload = self.generateAuthorCreationPayload(o.name,aliases);
let api = new mw.Api();
api.postWithToken("csrf", payload ).done(function( data ) {
if ( (data.success||0) != 1 ) {
alert ( "Item creation failed" ) ;
return ;
}
let q = data.entity.id ;
self.changeAutorToP50Here(o,q);
} ).fail( function(code, token_data) {
console.log(code,token_data);
alert("Error creating new author item: "+code);
} );
};
div.append(a);
a = document.createElement('a');
a.style='margin-right:0.2rem;';
a.innerHTML='⧉';
a.href='#';
a.title='Assign an existing author item';
a.onclick = function(){
let q = prompt("Item ID of the author");
if ( q===null || q=='' ) return;
q = 'Q'+q.replace(/\D/g,'');
if ( q=='Q' ) return alert("Invalid Qid");
self.changeAutorToP50Here(o,q.toUpperCase());
};
div.append(a);
a = document.createElement('a');
a.innerHTML='🔎';
a.href='https://www.wikidata.org/w/index.php?search=&search='+o.name+' haswbstatement:P31=Q5&title=Special%3ASearch&ns0=1';
a.title='Search for this name';
a.target='_blank';
div.append(a);
$('[id="'+o.statement_id+'"] div.wikibase-statementview-mainsnak div.wikibase-snakview-body').each(function(dummy,svbody){
svbody.append(div);
});
},
checkDoubleAuthors : function () {
let self = this ;
let ans = self.getAuthorNamesFromInterfaceWithSerial('P2093') ;
let p50 = self.getAuthorNamesFromInterfaceWithSerial('P50') ;
let serials = {} ;
self.name_collisions = [] ;
ans.filter((author_serial)=>author_serial.serial!='').forEach((author_serial)=>{
self.addAuthorNameLinks(author_serial);
});
ans.filter((author_serial)=>author_serial.serial!='').forEach((author_serial)=>{
if ( typeof serials[author_serial.serial]=='undefined') serials[author_serial.serial] = {ans:[],p50:[]};
serials[author_serial.serial].ans.push(author_serial);
});
p50.filter((author_serial)=>author_serial.serial!='').forEach((author_serial)=>{
if ( typeof serials[author_serial.serial]=='undefined') serials[author_serial.serial] = {ans:[],p50:[]};
serials[author_serial.serial].p50.push(author_serial);
});
self.name_collisions = Object
.keys(serials)
.filter((key)=>serials[key].ans.length>0 && serials[key].p50.length>0)
.map((key)=>{return {serial:key,ans:serials[key].ans,p50:serials[key].p50}}) ;
self.ans = ans;
} ,
getAuthorNamesFromInterfaceWithSerial : function ( property ) {
let self = this ;
let ret = [] ;
let selector = 'div.wikibase-statementview-mainsnak div.wikibase-snakview-value' ;
if ( property == 'P50' ) selector += ' a' ;
$('#'+property+' div.wikibase-statementview').each ( function ( dummy , statement ) {
let statement_id = $(statement).attr('id');
let container = $(statement).find('div.wikibase-statementview-mainsnak-container').get(0);
let name = $(container).find(selector).text().trim();
let serial = '' ;
$(container).find('div.wikibase-statementview-qualifiers div.wikibase-snaklistview').each(function(dummy,qualifier){
if ( $(qualifier).find('a[title="Property:P1545"]').length == 0 ) return ;
serial = $(qualifier).find('div.wikibase-snakview-value-container').text().trim();
});
let entry = {name:name,serial:serial,q:'',statement_id:statement_id,checked:true} ;
if ( property == 'P50' ) entry.q = $(container).find(selector).attr('title').trim() ;
ret.push(entry);
} ) ;
return ret ;
} ,
assignQtoAuthor : function ( author_key ) {
let self = this ;
let q = 'Q'+self.authors[author_key].tmp_q.replace(/\D/g,'') ;
if ( q == 'Q' ) {
alert ( "Invalid Qxxx" ) ;
return ;
}
let occurrences = self.authors[author_key].occurrences.filter((o)=>o.checked);
// chain will contain the "serialized" promises
let chain = Promise.resolve();
occurrences.forEach ( (o) => {
promise = new Promise((resolve) => {
o.q = q ;
self.changeOccurrenceStringNameToP50(o,function(){
setTimeout(resolve,200); // Small delay to allow caches to update etc
}) ;
});
chain = chain.then(() => promise);
} ) ;
// Start the "serialized" chain
chain.then(() => { // chain is done
self.authors[author_key].tmp_q = '' ;
});
} ,
changeOccurrenceStringNameToP50 : function ( o , resolve ) { // o=occurrence {q,paper_q,name,checked}
let self = this ;
// Get the P2093 statement to replace
let item = wdutil.entity_cache[o.paper_q] ;
if ( typeof item=='undefined' ) return ;
let p2093s = ((item.claims||[])["P2093"]||[]).filter((claim)=>((((claim||{}).mainsnak||{}).datavalue||{}).value||'')==o.name);
if ( p2093s.length == 0 ) { o.status = "Author '"+o.name+"' in "+o.paper_q+" not found"; return resolve(); } ;
if ( p2093s.length > 1 ) { o.status = "More than one author '"+o.name+"' in "+o.paper_q; return resolve(); } ;
let p2093 = p2093s[0] ;
// Make sure this author item is not already used as P50 in the paper
let p50s = ((item.claims||[])["P50"]||[]).filter((claim)=>(((((claim||{}).mainsnak||{}).datavalue||{}).value||{}).id||'')==o.q);
if ( p50s.length > 0 ) { o.status = "Author '"+o.name+"' already has a P50 in "+o.paper_q; return resolve(); } ;
// Construct P50 creation and P2093 removal
let p50_value = {
"entity-type": "item",
"numeric-id": o.q.replace(/\D/g,''),
"id": o.q
} ;
let data = { claims:[
{
mainsnak:{
snaktype: 'value',
property: "P50",
datavalue: {
value: p50_value,
type: "wikibase-entityid"
},
},
type: 'statement',
rank: 'normal'
} ,
{ id: p2093.id, remove: "" } // Remove P2093
] } ;
if ( typeof p2093.references!='undefined' ) {
p2093.references.forEach((ref)=>{
if ( typeof ref.hash!='undefined') delete ref['hash'] ;
if ( typeof ref['snaks-order']!='undefined') delete ref['snaks-order'] ;
Object.keys(ref.snaks).forEach((property)=>{
ref.snaks[property].forEach((snak)=>{
if ( typeof snak.hash!='undefined') delete snak['hash'] ;
});
});
});
data.claims[0].references = p2093.references ;
}
if ( typeof p2093.qualifiers!='undefined' ) {
// Flatten and remove IDs
let qualifiers = [] ;
Object.keys(p2093.qualifiers).forEach((property) => {
let subqs = p2093.qualifiers[property].map((subq)=>{
if ( typeof subq.id!='undefined') delete subq['id'];
if ( typeof subq.hash!='undefined') delete subq['hash'];
return subq
}) ;
qualifiers = qualifiers.concat(subqs);
})
qualifiers.push({
datatype: 'string',
snaktype: 'value',
property: 'P1932',
datavalue: {
value: o.name,
type: "string"
}
});
data.claims[0].qualifiers = qualifiers ;
}
let payload = {
action: 'wbeditentity',
id: o.paper_q,
data: JSON.stringify(data) ,
summary: "Changing author name '"+o.name+"' to P50:[["+o.q+"]] (via author_strings gadget)"
} ;
let api = new mw.Api();
api.postWithToken("csrf", payload ).done(function( new_entity ) {
if ( new_entity.success==1 ) { // Update cache
wdutil.entity_cache[o.paper_q] = new_entity.entity ;
}
if ( typeof o.checked!='undefined') delete o['checked'] ;
o.status = 'done' ;
// Set cached Q ID
let cache_key = self.getCacheAuthorKey(self.simplifyName(o.name)) ;
let expiry = 60*60*24*7 ; // 7 days
mw.storage.set ( cache_key , o.q , expiry ) ;
// /* TODO UI fixme
if ( o.q == wdutil.q ) {
let statement_html = wdutil.getValueHTML('P50',p50_value) ;
$('[id="'+p2093.id+'"]').remove();
// wdutil.createStatementElement("P50",statement_html,'dummy1','dummy2'); // TODO UUIDs
}
// */
resolve() ;
} ).fail( function(code, new_entity) {
o.status = code ;
console.log(code,new_entity);
resolve() ;
} );
resolve();
} ,
createAndLinkAuthor : function ( author_key ) {
let self = this ;
let occurrences = self.authors[author_key].occurrences.filter((o)=>o.checked);
let author_name_count = {} ;
occurrences.forEach(function(o){
if ( typeof author_name_count[o.name]=='undefined' ) author_name_count[o.name] = 1 ;
else author_name_count[o.name]++ ;
});
let anck = Object.keys(author_name_count);
if ( anck.length == 0 ) {
alert ( "Could not find any names" ) ;
return ;
}
anck.sort((a,b)=>{
if ( a.length==b.length ) return author_name_count[b]-author_name_count[a] ;
else return a.length<b.length ;
});
// Prepare data
let new_name = anck[0] ;
let aliases = self.authors[author_key].occurrences
.map((o)=>o.name) // Names
.filter((val, ind, arr) => arr.indexOf(val) === ind) // Unique
.filter((name) => name!=new_name)
.map((name) => { return {language:'en',value:name} } );
let payload = self.generateAuthorCreationPayload(new_name,aliases);
let api = new mw.Api();
api.postWithToken("csrf", payload ).done(function( data ) {
if ( (data.success||0) != 1 ) {
alert ( "Item creation failed" ) ;
return ;
}
let q = data.entity.id ;
self.authors[author_key].tmp_q = q ;
self.assignQtoAuthor(author_key);
} ).fail( function(code, token_data) {
console.log(code,token_data);
alert("Error creating new author item: "+code);
} );
} ,
generateAuthorCreationPayload : function(new_name,aliases) {
let new_data = {
labels:{en:{language:"en",value:new_name}},
descriptions:{en:{language:"en",value:"researcher"}},
claims:[
{
mainsnak:{
snaktype: 'value',
property: 'P31',
datavalue: {
value: {
"entity-type": "item",
"numeric-id": 5,
"id": "Q5"
},
type: 'wikibase-entityid'
},
},
type: 'statement',
rank: 'normal'
} ,
{
mainsnak:{
snaktype: 'value',
property: 'P106',
datavalue: {
value: {
"entity-type": "item",
"numeric-id": 1650915,
"id": "Q1650915"
},
type: 'wikibase-entityid'
},
},
type: 'statement',
rank: 'normal'
}
]
} ;
// Add aliases
if ( aliases.length>0 ) new_data.aliases = aliases ;
let payload = {
action: 'wbeditentity',
new: 'item',
data: JSON.stringify(new_data) ,
summary: "Creating new author item (via author_strings gadget)"
} ;
return payload;
},
onToggle : function (author_key,state) {
let self = this ;
self.authors[author_key].occurrences.forEach(function(o){
o.checked = state=='on'?true:(state=='off'?false:!o.checked)
}) ;
} ,
searchForAuthor : function (author_key) {
let query = author_key+' haswbstatement:P31=Q5' ; // haswbstatement:P496
let url = '/w/index.php?search=&search='+encodeURIComponent(query)+'&title=Special%3ASearch&ns0=1'
window.open(url, '_blank');
} ,
simplifyName : function ( name ) {
let ret = name.normalize('NFD').replace(/[\u0300-\u036f]/g, '').replace(/[^\x00-\x7F]/g, '') // ASCII-fy
.replace(/\S+\./g,' '); // Remove initials
if ( !ret.match(/^ *\S{3,} *\S{3,} *$/) ) ret = ret.replace(/(\b(\w{1,2})\b(\s|$))/g,'');
return ret.replace(/\s+/g,' ') // single spaces
.trim()
} ,
onPaperIDsLoaded : function ( callback ) {
let self = this ;
let to_load = Object.keys(self.paper_count);
to_load.sort((a,b)=> { // Most hits first
if ( self.paper_count[b] == self.paper_count[a] ) return a.replace(/\D/g,'')*1 - b.replace(/\D/g,'')*1 ;
else return self.paper_count[b] - self.paper_count[a]
});
to_load = to_load.slice(0, 45); // Max 45 papers so they will fit in a single query
to_load.push(wdutil.q); // Load this item's data as well
wdutil.loadEntities(to_load,function(){
self.papers = to_load
.filter((q)=>q!=wdutil.q)
.map((q)=>{return {q:q,count:self.paper_count[q],label:wdutil.getEntityName(q)} }) ;
to_load.forEach(function(q){self.paper_name[q]=wdutil.getEntityName(q)}) ;
self.groupNames(to_load) ;
self.loading = false ;
if ( typeof callback!='undefined') callback();
});
} ,
setTmpQ : function ( authors ) { // Uses mw.storage to retrieve recent name:Q mappings
let self = this ;
Object.keys(authors).forEach((author_key) => {
let cache_key = self.getCacheAuthorKey(author_key) ;
let recent_q = mw.storage.get ( cache_key ) ;
if ( recent_q ) authors[author_key].tmp_q = recent_q ;
}) ;
} ,
getCacheAuthorKey : function ( name ) {
return "author_strings_cache_"+name.toLowerCase();
} ,
isValidShortName : function ( short_name ) {
if ( short_name.length <= 4 ) return false ; // Do not use really short names
return true ;
} ,
groupNames : function ( paper_ids ) {
let self = this ;
let authors = {} ;
paper_ids.forEach ( function ( paper_q ) {
let item = wdutil.entity_cache[paper_q] ;
if ( typeof item == 'undefined' ) return ;
let author_string_names = wdutil.getValuesForProperty(item,'P2093') ;
author_string_names.forEach ( function ( name ) {
let short_name = self.simplifyName(name).toLowerCase();
if ( !self.isValidShortName(short_name) ) return ;
if ( typeof authors[short_name]=='undefined' ) authors[short_name] = { short_name:short_name , occurrences:[] , tmp_q:'' } ;
authors[short_name].occurrences.push({paper_q:paper_q,name:name,checked:true});
} ) ;
} ) ;
self.authors_order = Object.keys(authors) ;
self.setTmpQ(authors);
self.sortAuthors(authors);
self.authors = authors ;
if ( self.authors_order.length < 500 ) { // Re-rendering will otherwise almost-crash machine...
self.iterateAuthorSearch(JSON.parse(JSON.stringify(self.authors_order)));
}
} ,
sortAuthors : function ( authors ) {
let self = this ;
self.authors_order.sort((a,b)=> {
if ( authors[a].tmp_q=='' && authors[b].tmp_q!='' ) return 1 ;
if ( authors[a].tmp_q!='' && authors[b].tmp_q=='' ) return -1 ;
return authors[b].occurrences.length - authors[a].occurrences.length
} ) ;
} ,
iterateAuthorSearch : function ( author_keys ) {
let self = this ;
if ( author_keys.length == 0 ) return ;
let author_key = author_keys.shift();
self.searchForAuthorItems(author_key,function(ids){
self.author_search_results[author_key] = ids ;
self.iterateAuthorSearch(author_keys);
})
} ,
searchForAuthorItems : function ( author_key , resolve ) {
let self = this ;
if ( author_key.trim()=='' ) return Promise.resolve(author_key,[]);
let query = author_key+' haswbstatement:P31=Q5' ;
let params = {
action:'query',
list:'search',
srnamespace:0,
//srlimit:500,
srsearch:query,
format:'json'
} ;
let url = wdutil.api + '?' + new URLSearchParams(params) ;
return fetch(url)
.then(data => { return data.json() })
.then(data => { return (((data||{}).query||{}).search||{}).map(function(result){return result.title}); })
.then(ids => {resolve ( ids )})
} ,
permuteNamesAndQueryPapers : function ( names , callback ) {
let self = this ;
let promises = [] ;
for (let i=0; i+1<names.length; i++) {
for (let j=i+1; j<names.length; j++) {
let query = self.buildSearchPapersForTwoAuthorsQuery(names[i],names[j]) ;
promises.push(new Promise((resolve, reject) => {
self.searchForPaper(query,resolve);
}));
}
}
Promise.all(promises).then(()=>{callback()});
} ,
searchForPaper : function ( query , resolve ) {
let self = this ;
$.get(wdutil.api,{
action:'query',
list:'search',
srnamespace:0,
srlimit:500,
srsearch:query,
format:'json'
},function(d){
let paper_ids = (((d||{}).query||{}).search||{})
.map(function(result){return result.title})
.filter(function(q){return q!=wdutil.q});
paper_ids = self.uArray(paper_ids); // Paranoia
paper_ids.forEach(function(q){
if ( typeof self.paper_count[q]=='undefined' ) self.paper_count[q]=0 ;
self.paper_count[q]++;
});
resolve();
}) ;
} ,
buildSearchPapersForTwoAuthorsQuery : function ( name1 , name2 ) {
let query = 'haswbstatement:P31=Q13442814 ' ;
if (name1.indexOf(' ') == -1) query += name1 ;
else query += '"'+name1+'"' ;
query += ' ' ;
if (name2.indexOf(' ') == -1) query += name2 ;
else query += '"'+name2+'"' ;
return query ;
} ,
// Makes an array unique while maintaining sort order
uArray: function (array) {
let out = [];
for (let i=0, len=array.length; i<len; i++)
if (out.indexOf(array[i]) === -1)
out.push(array[i]);
return out;
}
},
components: {
CdxButton,
CdxCheckbox,
CdxTextInput,
},
};
if ( $('#P496,#P1153,#P1960,#P2798,#P106 a[title="Q1650915"]').length==0 && $('#P31 a[title="Q5"]').length > 0 ) { // Human but not researcher
let portletLink = mw.util.addPortletLink( 'p-tb', '#', 'Author strings','wikitext-wd_as');
$(portletLink).click ( function () {
wdutil_app.addTab({
name: 'author_strings',
label: 'Author strings',
},function(id){
const author_strings_app = Vue.createMwApp(AuthorStringsApp);
author_strings_app.mount(id);
});
return false ;
} ) ;
return false ;
}
wdutil_app.addTab({
name: 'author_strings',
label: 'Author strings',
},function(id){
const author_strings_app = Vue.createMwApp(AuthorStringsApp);
author_strings_app.mount(id);
});
return false ;
} ) ;
};
var wdutil_app ;
//mw.loader.load('https://wikidata-todo.toolforge.org/wdutils.js');
mw.loader.load('https://www.wikidata.org/w/index.php?title=User:Magnus_Manske/wdutil.js&action=raw&ctype=text/javascript');
if ( typeof wdutil!='undefined' ) wdutil.loadCallback(author_strings);
else {
var wdutil_loaded_callbacks ;
if ( typeof wdutil_loaded_callbacks=='undefined' ) wdutil_loaded_callbacks = [] ;
wdutil_loaded_callbacks.push(author_strings);
}
// </nowiki>