Участник:Putnik/wikidataInfoboxExport.js: различия между версиями
Перейти к навигации
Перейти к поиску
Содержимое удалено Содержимое добавлено
Putnik (обсуждение | вклад) +chv language (Q33348) |
Putnik (обсуждение | вклад) unique monolingual values |
||
Строка 21: | Строка 21: | ||
languages.unshift( contentLanguage ); |
languages.unshift( contentLanguage ); |
||
languages.unshift( userLanguage ); |
languages.unshift( userLanguage ); |
||
languages = $. |
languages = $.uniqueSort( languages ); |
||
// Main config |
// Main config |
||
Строка 444: | Строка 444: | ||
} ); |
} ); |
||
languages = $.merge( languages, _this.config.languages ); |
languages = $.merge( languages, _this.config.languages ); |
||
languages = $. |
languages = $.uniqueSort( languages ); |
||
var sites = titles.map( function ( item ) { |
var sites = titles.map( function ( item ) { |
||
Строка 654: | Строка 654: | ||
} |
} |
||
} |
} |
||
titles = $. |
titles = $.uniqueSort( titles ); |
||
} |
} |
||
if ( redirects.length ) { |
if ( redirects.length ) { |
||
Строка 1039: | Строка 1039: | ||
} |
} |
||
for ( var idx = 0; idx < $. |
for ( var idx = 0; idx < $.uniqueSort( units ).length; idx += 50) { |
||
_this.wdApi.get( { |
_this.wdApi.get( { |
||
action: 'wbgetentities', |
action: 'wbgetentities', |
||
languages: _this.config.languages, |
languages: _this.config.languages, |
||
props: [ 'labels', 'descriptions', 'aliases', 'claims' ], |
props: [ 'labels', 'descriptions', 'aliases', 'claims' ], |
||
ids: $. |
ids: $.uniqueSort( units ).slice( idx, idx + 50 ) |
||
} ).done( function ( unitData ) { |
} ).done( function ( unitData ) { |
||
if ( !unitData.success ) { |
if ( !unitData.success ) { |
||
Строка 1094: | Строка 1094: | ||
} |
} |
||
} |
} |
||
_this.config.units[ unitId ].search = $. |
_this.config.units[ unitId ].search = $.uniqueSort( unitSearch ); |
||
_this.saveConfig(); |
_this.saveConfig(); |
||
Строка 1377: | Строка 1377: | ||
} |
} |
||
} |
} |
||
var valueLanguages = []; |
|||
for ( var i in values ) { |
for ( var i in values ) { |
||
var valueLanguage = values[ i ].wd.value.language; |
|||
if ( valueLanguages.indexOf( valueLanguage ) > -1 ) { |
|||
continue; |
|||
} |
|||
valueLanguages.push( valueLanguage ); |
|||
values[ i ].label = $( '<span>' ) |
values[ i ].label = $( '<span>' ) |
||
.append( $( '<span>' ).css( 'color', '#666' ).text( '(' + |
.append( $( '<span>' ).css( 'color', '#666' ).text( '(' + valueLanguage + ') ' ) ) |
||
.append( $( '<strong>' ).text( values[ i ].wd.value.text ) ); |
.append( $( '<strong>' ).text( values[ i ].wd.value.text ) ); |
||
} |
} |
||
values = values.filter( function( item ) { |
|||
return item.label !== undefined; |
|||
} ); |
|||
break; |
break; |
||
Строка 1527: | Строка 1536: | ||
} |
} |
||
values = $. |
values = $.uniqueSort( values ); |
||
_this.dialog( $field, propertyId, values, _this.getReference( $field ) ); |
_this.dialog( $field, propertyId, values, _this.getReference( $field ) ); |
||
}; |
}; |
Версия от 21:16, 18 июня 2019
/**
* Quick Wikidata Infobox Export.
*
* Gadget for export information from infoboxes to Wikidata.
* The export window is shown by double clicking.
*/
( function ( mw, $ ) {
var wikidataInfoboxExport = function() {
var _this = this;
this.months = [
'january', 'february', 'march', 'april', 'may', 'june',
'july', 'august', 'september', 'october', 'november', 'december'
];
this.monthsGen = months;
// Site and user language setup
var contentLanguage = mw.config.get( 'wgContentLanguage' );
var userLanguage = mw.user.options.get( 'language' ) || contentLanguage;
var languages = [ 'en' ];
languages.unshift( contentLanguage );
languages.unshift( userLanguage );
languages = $.uniqueSort( languages );
// Main config
this.config = Object.assign( {
version: '2.0.0',
project: mw.config.get( 'wgDBname' ),
language: userLanguage,
userLanguage: userLanguage,
contentLanguage: contentLanguage,
languages: languages,
storageKey: 'infoboxExportConfig',
references: {},
units: {},
fixedValues: [],
misLang: {
ady: 'Q27776',
ain: 'Q20968488',
alt: 'Q1991779',
atv: 'Q2640863',
chm: 'Q973685',
chv: 'Q33348',
ckt: 'Q33170',
enf: 'Q29942',
evn: 'Q30004',
inh: 'Q33509',
izh: 'Q33559',
jdt: 'Q56495',
kjh: 'Q33575',
krl: 'Q33557',
kum: 'Q36209',
orv: 'Q35228',
otk: 'Q34988',
pdt: 'Q1751432',
sjd: 'Q33656',
sjt: 'Q36656',
sga: 'Q35308',
sma: 'Q13293',
vot: 'Q32858',
yrk: 'Q36452'
},
centuries: [ 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII',
'VIII', 'IX', 'X', 'XI', 'XII', 'XIII', 'XIV', 'XV',
'XVI', 'XVII', 'XVIII', 'XIX', 'XX', 'XXI', 'XXII' ],
}, window.wieConfig || {} );
this.i18n = {};
this.api = null;
this.wdApi = null;
this.commonsApi = null;
this.baseRevId;
this.propertyIds = [ 'P2076', 'P2077' ]; // Temperature and pressure for qualifiers
this.typesMapping = {
'commonsMedia': 'string',
'external-id': 'string',
'url': 'string',
'wikibase-item': 'wikibase-entityid'
};
this.alreadyExistingItems = {};
this.windowManager;
/**
* Compares the values of the infobox and Wikidata
*/
this.canExportValue = function ( $field, claims, callbackIfCan ) {
if ( !claims || !( claims.length ) ) {
// Can't export only if it is large local image
if ( !( $field.find( '.image img[src*="/wikipedia/' + _this.config.contentLanguage + '/"]' ).width() >= 80 ) ) {
callbackIfCan();
}
return;
}
switch ( claims[ 0 ].mainsnak.datatype ) {
case 'quantity':
for ( var i = 0; i < claims.length; i++ ) {
var parsedTime = _this.createTimeSnak( ( $field.text().match( /\(([^)]*\d\d\d\d)[,)\s]/ ) || [] )[ 1 ] );
if ( parsedTime && ( claims[ i ].qualifiers || {} ).P585 ) {
var claimPrecision = claims[ i ].qualifiers.P585[ 0 ].datavalue.value.precision;
if ( parsedTime.precision < claimPrecision ) {
claims[ i ].qualifiers.P585[ 0 ].datavalue.value.precision = parsedTime.precision;
} else if ( parsedTime.precision > claimPrecision ) { // FIXME: Specify the date in Wikidata later
parsedTime.precision = claimPrecision;
}
var p585 = parsedTime ? _this.formatDataValue( {
type: 'time',
value: parsedTime
} )[ 0 ].innerText : '';
if ( _this.formatDataValue( claims[ i ].qualifiers.P585[ 0 ].datavalue )[ 0 ].innerText !== p585 ) {
claims[ i ].qualifiers.P585[ 0 ].datavalue.value.precision = claimPrecision;
continue;
}
}
return;
}
callbackIfCan( true );
break;
case 'wikibase-item':
value = _this.parseItems( $field, $field, function ( values ) {
var duplicates = [];
for ( var i = 0; i < values.length; i++ ) {
for ( var j = 0; j < claims.length; j++ ) {
if ( values[ i ].wd.value.id === claims[ j ].mainsnak.datavalue.value.id ) {
duplicates.push( values[ i ].wd.value.id );
}
}
}
if ( duplicates.length < values.length ) {
if ( duplicates.length > 0 ) {
var propertyId = claims[ 0 ].mainsnak.property;
_this.alreadyExistingItems[ propertyId ] = duplicates;
if ( propertyId === 'P166' && values.length === claims.length )
return;
}
if ( claims.length > 0 ) {
if ( claims[ 0 ].mainsnak.property === 'P19' || claims[ 0 ].mainsnak.property === 'P20' )
return;
}
callbackIfCan( true );
}
} );
}
// By default we can't export if there are claims already
};
/**
* Claim's GUID generation
*/
this.claimGuid = function ( entityId ) {
var getRandomHex = function ( min, max ) {
return ( Math.floor( Math.random() * ( max - min + 1 ) ) + min ).toString( 16 );
};
var template = 'xx-x-x-x-xxx';
var guid = '';
for ( var i = 0; i < template.length; i++ ) {
if ( template.charAt( i ) === '-' ) {
guid += '-';
continue;
}
var hex;
if ( i === 3 ) {
hex = getRandomHex( 16384, 20479 );
} else if ( i === 4 ) {
hex = getRandomHex( 32768, 49151 );
} else {
hex = getRandomHex( 0, 65535 );
}
while ( hex.length < 4 ) {
hex = '0' + hex;
}
guid += hex;
}
return entityId + '$' + guid;
};
/**
* Formate dates as datavalue for Wikidata
*/
this.createTimeSnak = function ( timestamp, forceJulian ) {
if ( !timestamp ) {
return;
}
var result = { timezone: 0, before: 0, after: 0 };
var isoDate;
var dateParts;
if ( timestamp.match ( /\s\([^\)]*\)\s/ ) ) {
forceJulian = true;
}
timestamp = timestamp.replace( /\([^\)]*\)/, '' ).trim();
var isBce = false;
var bceMatch = timestamp.match( _this.config.reBce );
if ( bceMatch ) {
isBce = true;
timestamp = timestamp.replace( bceMatch[ 0 ], '' ).trim();
} else {
var ceMatch = timestamp.match( _this.config.reCe );
if ( ceMatch ) {
timestamp = timestamp.replace( ceMatch[ 0 ], '' ).trim();
}
}
if ( dateParts = timestamp.match( _this.config.reCentury ) ) {
isoDate = new Date( 0 );
isoDate.setFullYear( _this.config.centuries.indexOf( dateParts[ 1 ].toUpperCase() ) * 100 + 1 );
result.precision = 7;
} else if ( dateParts = timestamp.match( _this.config.reMonthYear ) ) {
isoDate = new Date( Date.UTC( dateParts[ 2 ], _this.months.indexOf( dateParts[ 1 ] ) ) );
result.precision = 10;
} else if ( dateParts = timestamp.match( _this.config.reTextDate ) ) {
isoDate = new Date( Date.UTC( dateParts[ 3 ], _this.monthsGen.indexOf( dateParts[ 2 ] ), dateParts[ 1 ] ) );
result.precision = 11;
} else if ( dateParts = timestamp.match( _this.config.reDotDate ) ) {
isoDate = new Date( Date.UTC( dateParts[ 3 ] < 100 ? 1900 + parseInt( dateParts[ 3 ] ) : dateParts[ 3 ], dateParts[ 2 ] - 1, dateParts[ 1 ] ) );
result.precision = 11;
} else if ( dateParts = timestamp.match( _this.config.reIsoDate ) ) {
isoDate = new Date( Date.UTC( dateParts[ 1 ] < 100 ? 1900 + parseInt( dateParts[ 1 ] ) : dateParts[ 1 ], dateParts[ 2 ] - 1, dateParts[ 3 ] ) );
result.precision = 11;
} else if ( dateParts = timestamp.match( _this.config.reDecade ) ) {
isoDate = new Date( Date.UTC( dateParts[ 1 ], 0 ) );
result.precision = 8;
} else if ( dateParts = timestamp.match( _this.config.reYear ) ) {
isoDate = new Date( Date.UTC( dateParts[ 1 ], 0 ) );
result.precision = 9;
} else if ( timestamp.match( _this.config.rePresent ) ) {
return 'novalue';
} else if ( timestamp.match( _this.config.reUnknown ) ) {
return 'somevalue';
} else {
return;
}
try {
isoDate.setUTCHours( 0 );
isoDate.setUTCMinutes( 0 );
isoDate.setUTCSeconds( 0 );
result.time = ( isBce ? '-' : '+' ) + isoDate.toISOString().replace( /\.000Z/, 'Z' );
} catch ( e ) {
return;
}
if ( result.precision < 11 ) {
result.time = result.time.replace( /-\d\dT/, '-00T' );
}
if ( result.precision < 10 ) {
result.time = result.time.replace( /-\d\d-/, '-00-' );
}
result.calendarmodel = 'http://www.wikidata.org/entity/Q' +
( forceJulian || isoDate < new Date( Date.UTC( 1582, 9, 15 ) ) ? '1985786' : '1985727' );
return result;
};
/**
* Error display
*/
this.errorDialog = function ( title, message ) {
var errorDialog = new OO.ui.MessageDialog();
_this.windowManager.addWindows( [ errorDialog ] );
_this.windowManager.openWindow( errorDialog, {
title: title,
message: message
} );
};
/**
* Extract reference URL
*/
this.getReference = function ( $field ) {
var references = [];
var $notes = $field.find( 'sup.reference a' );
for ( var i = 0; i < $notes.length; i++ ) {
var $externalLinks = $( decodeURIComponent($notes[ i ].hash).replace( /[!"$%&'()*+,.\/:;<=>?@[\\\]^`{|}~]/g, '\\$&' ) + ' a[rel="nofollow"]' );
for ( var j = 0; j < $externalLinks.length; j++ ) {
var $externalLink = $( $externalLinks.get( j ) );
if ( !$externalLink.attr( 'href' ).match( /(wikipedia.org|webcitation.org|archive.is)/ ) ) {
var source = {
snaks: {
P854: [ {
property: 'P854',
datatype: 'url',
snaktype: 'value',
datavalue: {
type: 'string',
value: $externalLink.attr( 'href' ).replace( /^\/\//, 'https://' )
}
} ]
}
};
// P813
if ( _this.config.markChecked !== '' ) {
var $accessed = $externalLinks.parent().find( 'small:contains("' + _this.config.markChecked + '")' );
if ( $accessed.length ) {
var accessDate = _this.createTimeSnak( $accessed.first().text() );
if ( accessDate ) {
source.snaks.P813 = [ {
property: 'P813',
datatype: 'time',
snaktype: 'value',
datavalue: {
type: 'time',
value: accessDate
}
} ];
}
}
}
// P1065 + P2960
if ( _this.config.markArchived !== '' ) {
var $archiveLinks = $externalLinks.filter( 'a:contains("' + _this.config.markArchived + '")' );
if ( $archiveLinks.length ) {
var $archiveLink = $archiveLinks.first();
source.snaks.P1065 = [ {
property: 'P1065',
datatype: 'url',
snaktype: 'value',
datavalue: {
type: 'string',
value: $archiveLink.attr( 'href' ).replace( /^\/\//, 'https://' )
}
} ];
var archiveDate = _this.createTimeSnak( $archiveLink.parent().text().replace( _this.config.markArchived, '' ).trim() );
if ( archiveDate ) {
source.snaks.P2960 = [ {
property: 'P2960',
datatype: 'time',
snaktype: 'value',
datavalue: {
type: 'time',
value: archiveDate
}
} ];
}
}
}
references.push( source );
break;
}
}
}
references.push( { snaks: _this.config.references } );
return references;
};
/**
* Format sources for display
*/
this.formatDomains = function ( references ) {
var $result = $( '<sup>' );
for ( var i = 0; i < references.length; i++ ) {
var p854 = references[ i ].snaks.P854;
if ( p854 ) {
var domain = p854[ 0 ].datavalue.value.replace( 'http://', '' ).replace( 'https://', '' ).replace( 'www.', '' );
if ( domain.indexOf( '/' ) > 0 ) {
domain = domain.substr( 0, domain.indexOf( '/' ) );
}
$result.append( $( '<a>' ).attr( 'href', p854[ 0 ].datavalue.value ).text( '[' + domain + ']' ) );
}
}
return $result;
};
/**
* Formatting wikidata values for display to the user
*/
this.formatDataValue = function ( datavalue ) {
var $label = $( '<span>' );
switch ( datavalue.type ) {
case 'time':
var bceMark = ( datavalue.value.time.charAt( 0 ) === '-' ? _this.i18n.bcePostfix : '' );
if ( datavalue.value.precision === 7 ) {
$label.text( _this.config.centuries[ Math.floor( (datavalue.value.time.substr( 1, 4 ) - 1) / 100 ) ] + _this.i18n.agePostfix + bceMark );
break;
}
var options = {};
if ( datavalue.value.precision > 7 ) {
options.year = 'numeric';
}
if ( datavalue.value.precision > 9 ) {
options.month = 'long';
}
if ( datavalue.value.precision > 10 ) {
options.day = 'numeric';
}
var parsedDate = new Date( Date.parse( datavalue.value.time.substring( 1 ).replace( /-00/g, '-01' ) ) );
$label.text( parsedDate.toLocaleString( _this.config.userLanguage, options ) + ( datavalue.value.precision === 8 ? _this.i18n.decadePostfix : '' ) + bceMark );
break;
case 'quantity':
$label.append( $( '<strong>' ).text( datavalue.value.amount ) );
if ( datavalue.value.bound ) {
$label.append( $( '<span>' ).text( ' ± ' + datavalue.value.bound ) );
}
if ( datavalue.value.unit !== '1' ) {
var unitId = datavalue.value.unit.substr( datavalue.value.unit.indexOf( 'Q' ) );
var name = ( ( _this.config.units[ unitId ] || {} ).label || {} ).value || unitId;
var description = ( ( _this.config.units[ unitId ] || {} ).description || {} ).value || _this.i18n.noDescription;
$label.append( ' ' ).append( $( '<abbr>' ).attr( 'title', description ).text( name ) );
}
break;
case 'wikibase-entityid':
$label.append( $( '<strong>' ).text( datavalue.value.label ? datavalue.value.label : datavalue.value.id ) )
.append( datavalue.value.description ? ' — ' + datavalue.value.description : '' );
break;
}
for ( var propertyId in datavalue.qualifiers ) {
if ( !datavalue.qualifiers.hasOwnProperty( propertyId ) ) {
continue;
}
if ( propertyId === 'P1480' && datavalue.qualifiers[ propertyId ][ 0 ].datavalue.value.id === 'Q5727902' ) {
$label.prepend( $( '<abbr>' ).attr( 'title', _this.i18n.circaTitle ).text( _this.i18n.circaPrefix ), ' ' );
} else {
var formatted = _this.formatDataValue( datavalue.qualifiers[ propertyId ][ 0 ].datavalue );
if ( formatted && $( '<span>' ).append( formatted ).text() ) {
$label.append( $( '<span>' ).text( ' (' ).append( formatted ).append( ')' ) );
}
}
}
return $label;
};
this.getWikidataIds = function ( titles, callback ) {
var languages = titles.map( function ( item ) {
return item.language;
} );
languages = $.merge( languages, _this.config.languages );
languages = $.uniqueSort( languages );
var sites = titles.map( function ( item ) {
return item.project;
} );
_this.wdApi.get( {
action: 'wbgetentities',
sites: sites,
languages: languages,
props: [ 'labels', 'descriptions', 'claims' ],
titles: titles.map( function ( item ) {
return item.label;
} )
} ).done( function ( data ) {
if ( data.success ) {
var valuesObj = {};
var value;
for ( var entityId in data.entities ) {
if ( !data.entities.hasOwnProperty( entityId ) || !entityId.match( /^Q/ ) ) {
continue;
}
var entity = data.entities[ entityId ];
var label = entity.labels[ _this.config.userLanguage ] || entity.labels.en || entity.labels[ Object.keys( entity.labels )[ 0 ] ] || '';
var description = entity.descriptions[ _this.config.userLanguage ] || entity.descriptions.en || entity.descriptions[ Object.keys( entity.descriptions )[ 0 ] ] || '';
if ( ( ( ( ( ( ( ( entity || {} ).claims || {} ).P31 || [] )[ 0 ] || {} ).mainsnak || {} ).datavalue || {} ).value || {} ).id === 'Q4167410' ) {
continue; // skip disambigs
}
var subclassFound = false;
var subclassEntity = null;
for ( var candidateId in data.entities ) {
if ( !data.entities.hasOwnProperty( candidateId ) || !candidateId.match( /^Q/ ) || entityId === candidateId ) {
continue;
}
subclassFound = [ 'P17', 'P31', 'P131', 'P279', 'P361' ].find( function ( propertyId ) {
var values = ( ( ( data.entities[ candidateId ] || {} ).claims || {} )[ propertyId ] || [] );
return values.find( function ( statement ) {
var result = ( ( ( statement.mainsnak || {} ).datavalue || {} ).value || {} ).id === entityId;
if ( result ) {
subclassEntity = data.entities[ candidateId ];
}
return result;
} );
} );
if ( subclassFound ) {
break;
}
}
if ( subclassFound ) {
if ( subclassEntity ) {
var subclassLabel = subclassEntity.labels[ _this.config.userLanguage ] || subclassEntity.labels.en || subclassEntity.labels[ Object.keys( subclassEntity.labels )[ 0 ] ];
var text = _this.i18n.morePreciseValue
.replace( '$1', label.value )
.replace( '$2', subclassLabel.value );
mw.notify(text , {
type: 'warn',
tag: 'wikidataInfoboxExport-warn-precise'
} );
}
continue; // skip values for which there are more accurate values
}
value = {
wd: {
type: 'wikibase-entityid',
value: {
id: entityId,
label: label ? label.value : label,
description: description ? description.value : description
}
}
};
if ( label ) {
var results = titles.filter( function ( item ) {
return item.label.toLowerCase() === label.value.toLowerCase();
} );
if ( results.length === 1 ) {
value.wd.qualifiers = results[ 0 ].qualifiers;
}
}
value.label = _this.formatDataValue( value.wd );
delete value.wd.value.label;
delete value.wd.value.description;
valuesObj[ entityId ] = value;
}
callback( valuesObj );
}
} );
};
this.parseItems = function ( $content, $wrapper, callback ) {
var processWbGetItems = function ( valuesObj ) {
var values = $.map( valuesObj, function( value, index ) {
return [ value ];
} );
if ( values.length === 1 ) {
value = values.pop();
_this.addQualifiers( $wrapper, value.wd, value.label, function( value ) {
callback( [ value ] );
} );
} else if ( callback ) {
callback( values );
}
};
var titles = [];
for ( var k = 0; k < _this.config.fixedValues.length; k++ ) {
var fixedValue = _this.config.fixedValues[ k ];
if ( $content.attr( 'data-wikidata-property-id' ) === fixedValue.property &&
$content.text().match( fixedValue.regexp )
) {
var result = { success: true, entities: {} };
result.entities[ fixedValue.item ] = {
labels: { ru: { value: fixedValue.label } },
descriptions: {}
};
processWbGetItems( result );
return;
}
}
var $links = $content.find( 'a[title][class!=image][class!=new]' );
var redirects = [];
if ( $links.length ) {
for ( var j = 0; j < $links.length; j++ ) {
var $link = $( $links[ j ] );
if ( $link.parents( '[data-wikidata-qualifier-id]' ).length ) {
continue;
}
var extractedUrl = decodeURIComponent( $link.attr( 'href' ) ).replace( /^.*\/wiki\//, '' );
if ( extractedUrl ) {
extractedUrl = extractedUrl.replace( /_/g, ' ' ).trim();
var value = {
label: extractedUrl.charAt( 0 ).toUpperCase() + extractedUrl.substr( 1, extractedUrl.length - 1 ),
language: _this.config.contentLanguage,
project: _this.config.project,
qualifiers: {}
};
var match = $links[ j ].innerHTML.match( _this.config.reSinceYear );
if ( !match ) {
match = $links[ j ].innerHTML.match( _this.config.reUntilYear );
}
var extractedYear = match ? _this.createTimeSnak( match[ 1 ] ) : _this.createTimeSnak( ( $links[ j ].nextSibling || {} ).textContent );
if ( extractedYear ) {
value.qualifiers.P585 = [ {
property: 'P585',
datatype: 'time',
snaktype: 'value',
datavalue: {
type: 'time',
value: extractedYear
}
} ];
}
if ( $link.hasClass( 'extiw' ) ) {
var m = $links[ j ].getAttribute( 'href' ).match( /^https:\/\/([a-z\-]+)\.(wik[^\.]+)\./ );
if ( m && m[ 2 ] !== 'wikimedia' ) {
value.language = m[ 1 ];
value.project = m[ 1 ] + m[ 2 ].replace( 'wikipedia', 'wiki' );
}
}
if ( $link.hasClass( 'mw-redirect' ) ) {
redirects.push( extractedUrl );
}
titles.push( value );
if ( $( $links[ j ] ).find( 'img' ) ) {
redirects.push( extractedUrl );
}
}
}
} else if ( $content.text().trim() ) {
// If no links found try to search for articles by text value
var parts = $content.text().split( /[\n,;]+/ );
for ( var i in parts ) {
var year = '';
var articleTitle = parts[ i ].replace( /\([^)]*\)/, function ( match ) {
year = match.replace( /\(\)/, '' );
return '';
} ).trim();
if ( articleTitle ) {
var value = {
label: articleTitle.charAt( 0 ).toUpperCase() + articleTitle.substr( 1, articleTitle.length - 1 ),
language: _this.config.language,
project: _this.config.project,
qualifiers: {}
};
if ( _this.createTimeSnak( year ) ) {
value.qualifiers.P585 = [ {
property: 'P585',
datatype: 'time',
snaktype: 'value',
datavalue: {
type: 'time',
value: _this.createTimeSnak( year )
}
} ];
}
titles.push( value );
}
}
titles = $.uniqueSort( titles );
}
if ( redirects.length ) {
_this.api.get( {
action: 'query',
redirects: 1,
titles: redirects
} ).done( function ( data ) {
if ( data.query && data.query.redirects ) {
for ( var i = 0; i < data.query.redirects.length; i++ ) {
for ( var j = 0; j < titles.length; j++ ) {
var lcTitle = titles[ j ].label.substr( 0, 1 ).toLowerCase() + titles[ j ].label.substr( 1 );
var lcRedirect = data.query.redirects[ i ].from.substr( 0, 1 ).toLowerCase() + data.query.redirects[ i ].from.substr( 1 );
if ( lcTitle === lcRedirect ) {
titles.splice( j + 1, 0, {
label: data.query.redirects[ i ].to,
language: _this.config.contentLanguage,
project: _this.config.project,
year: titles[ j ].year
} );
j++;
}
}
}
}
_this.getWikidataIds( titles, processWbGetItems );
} );
} else {
_this.getWikidataIds( titles, processWbGetItems );
}
};
/**
* Parsing the number and (optionally) the accuracy
*/
this.parseQuantity = function ( text, forceInteger ) {
var out = {
value: {},
};
text = text.replace( /,/g, '.' ).replace( /[−–—]/g, '-' ).trim();
// Sourcing circumstances (P1480) = circa (Q5727902)
var circaMatch = text.match( _this.config.reCirca );
if ( circaMatch ) {
out.qualifiers = {
P1480: [ {
property: 'P1480',
snaktype: 'value',
datavalue: {
type: 'wikibase-entityid',
value: { id: 'Q5727902' }
}
} ],
};
text = text.replace( circaMatch[ 0 ], '' );
}
var magnitude = 0;
if ( text.match( _this.config.re10_3 ) ) {
magnitude += 3;
} else if ( text.match( _this.config.re10_6 ) ) {
magnitude += 6;
} else if ( text.match( _this.config.re10_9 ) ) {
magnitude += 9;
} else if ( text.match( _this.config.re10_12 ) ) {
magnitude += 12;
} else {
var match = text.match( /[\*|·]10(-?\d+)/ );
if ( match ) {
text = text.replace( /[\*|·]10(-?\d+)/, '' );
magnitude += parseInt( match[ 1 ] );
}
}
var decimals = text.split( '±' );
if ( magnitude === 0 && forceInteger ) {
decimals[ 0 ] = decimals[ 0 ].replace( /\./g, '' ).trim();
}
var amount;
var bound;
var interval = decimals[ 0 ].split( '-' );
if ( magnitude === 0 &&
decimals.length === 1 &&
interval.length === 2 &&
interval[ 0 ].length !== 0 &&
interval[ 1 ].length !== 0
) {
out.value.lowerBound = interval[ 0 ].replace( /[^0-9.+-]/g, '' );
out.value.upperBound = interval[ 1 ].replace( /[^0-9.+-]/g, '' );
parts = out.value.lowerBound.match( /(\d+)\.(\d+)/ );
fractional = parts ? parts[ 2 ].length : 0;
out.value.amount = ( ( parseFloat( out.value.upperBound ) +
parseFloat( out.value.lowerBound ) )/2 )
.toFixed( fractional + 1 );
out.value.bound = ( ( parseFloat( out.value.upperBound ) -
parseFloat( out.value.lowerBound ) )/2 )
.toFixed( fractional + 1 );
return out;
} else {
amount = parseFloat( decimals[ 0 ].replace( /[^0-9.+-]/g, '' ) );
}
if ( isNaN( amount ) ) {
return;
}
var parts = amount.toString().match( /(\d+)\.(\d+)/ );
var integral = parts ? parts[ 1 ].length : amount.toString().length;
var fractional = parts ? parts[ 2 ].length : 0;
if ( magnitude >= 0 ) {
if ( magnitude <= fractional ) {
out.value.amount = ( ( '1e' + magnitude ) * amount ).toFixed( fractional - magnitude );
} else {
out.value.amount = ( ( '1e' + fractional ) * amount ).toFixed( 0 ).replace( /$/, new Array( magnitude - fractional + 1 ).join( '0' ) );
}
} else {
if ( magnitude >= -integral ) {
out.value.amount = ( ( '1e' + magnitude ) * amount ).toFixed( fractional - magnitude );
} else {
out.value.amount = ( ( '1e-' + integral ) * amount ).toFixed( integral + fractional ).replace( /0\./, '0.' + new Array( -magnitude - integral + 1 ).join( '0' ) );
}
}
if ( decimals.length > 1 ) {
bound = parseFloat( decimals[ 1 ].replace( /[^0-9.+-]/g, '' ) );
}
if ( !isNaN( bound ) ) {
if ( decimals.length > 1 && decimals[ 1 ].indexOf( '%' ) > 0 ) {
bound = amount * bound / 100;
} else {
parts = bound.toString().match( /(\d+)\.(\d+)/ );
integral = parts ? parts[ 1 ].length : amount.toString().length;
fractional = parts ? parts[ 2 ].length : 0;
}
if ( magnitude >= 0 ) {
if ( magnitude <= fractional ) {
out.value.lowerBound = ( ( '1e' + magnitude ) * ( amount - bound ) ).toFixed( fractional - magnitude );
out.value.upperBound = ( ( '1e' + magnitude ) * ( amount + bound ) ).toFixed( fractional - magnitude );
out.value.bound = ( ( '1e' + magnitude ) * bound ).toFixed( fractional - magnitude ); // need to show it to user
} else {
out.value.lowerBound = ( ( '1e' + fractional) * ( amount - bound ) ).toFixed( 0 ).replace( /$/, new Array( magnitude - fractional + 1 ).join( '0' ) );
out.value.upperBound = ( ( '1e' + fractional) * ( amount + bound ) ).toFixed( 0 ).replace( /$/, new Array( magnitude - fractional + 1 ).join( '0' ) );
out.value.bound = ( ( '1e' + fractional ) * bound ).toFixed( 0 ).replace( /$/, new Array( magnitude - fractional + 1 ).join( '0' ) );
}
} else {
if ( magnitude >= -integral ) {
out.value.lowerBound = ( ( '1e' + magnitude ) * ( amount - bound ) ).toFixed( fractional - magnitude );
out.value.upperBound = ( ( '1e' + magnitude ) * ( amount + bound ) ).toFixed( fractional - magnitude );
out.value.bound = ( ( '1e' + magnitude ) * bound ).toFixed( fractional - magnitude );
} else {
out.value.lowerBound = ( ( '1e-' + integral ) * ( amount - bound ) ).toFixed( integral + fractional ).replace( /0\./, '0.' + new Array( -magnitude - integral + 1 ).join( '0' ) );
out.value.upperBound = ( ( '1e-' + integral ) * ( amount + bound ) ).toFixed( integral + fractional ).replace( /0\./, '0.' + new Array( -magnitude - integral + 1 ).join( '0' ) );
out.value.bound = ( ( '1e-' + integral ) * bound ).toFixed( integral + fractional ).replace( /0\./, '0.' + new Array( -magnitude - integral + 1 ).join( '0' ) );
}
}
}
return out;
};
/**
* Recognition of units of measurement in the infobox parameter and its label
*/
this.recognizeUnits = function ( text, units, label ) {
if ( Array.isArray( units ) && units.length === 0 ) {
return [ '1' ];
}
var result = [];
for ( var idx in units ) {
if ( !units.hasOwnProperty( idx ) ) {
continue;
}
var item = parseInt( idx ) >= 0 ? units[ idx ] : idx;
var search = _this.config.units[ item ].search;
for ( var j = 0; j < search.length; j++ ) {
var expr = search[ j ];
if ( search[ j ].charAt( 0 ) !== '^' ) {
expr = '[\\d\\s\\.]' + expr;
if ( search[ j ].length < 5 ) {
expr = expr + '\\.?$';
}
}
if ( text.match( new RegExp( expr ) ) ) {
result.push( item );
break;
} else if ( search[ j ].charAt( 0 ) !== '^' && label && label.match( new RegExp( '\\s' + search[ j ] + ':?$' ) ) ) {
result.push( item );
break;
}
}
}
return result;
};
/**
* Create all statements in Wikidata and mark properties exported
*/
this.createClaims = function ( propertyId, values, refUrl, revIds ) {
var value = values.shift();
revIds = revIds || [];
if ( !value ) {
// All statements are added - go to the tagging
_this.addTags( propertyId, revIds );
return;
} else {
value = JSON.parse( value );
}
if ( _this.config.properties[ propertyId ] === undefined ) {
mw.notify( _this.i18n.noPropertyData.replace( '$1', propertyId ), {
type: 'error',
tag: 'wikidataInfoboxExport-property-error'
} );
return;
}
var datatype = _this.config.properties[ propertyId ].datatype;
var mainsnak = value.value.toString().match( /^(novalue|somevalue)$/ ) ? {
snaktype: value.value,
property: propertyId
} : {
snaktype: 'value',
property: propertyId,
datavalue: {
type: _this.typesMapping[ datatype ] ? _this.typesMapping[ datatype ] : datatype,
value: value.value
}
};
var claim = {
type: 'statement',
mainsnak: mainsnak,
id: _this.claimGuid( mw.config.get( 'wgWikibaseItemId' ) ),
references: refUrl,
rank: 'normal'
};
if ( value.qualifiers ) {
claim.qualifiers = value.qualifiers;
}
_this.wdApi.postWithToken( 'csrf', {
action: 'wbsetclaim',
claim: JSON.stringify( claim ),
baserevid: _this.baseRevId
} ).done( function ( claimData ) {
if ( claimData.success ) {
var valuesLeftStr = values.length ? _this.i18n.valuesLeft.replace( '$1', values.length ) : '';
mw.notify( _this.i18n.valueSaved.replace( '$1', propertyId ) + valuesLeftStr, {
tag: 'wikidataInfoboxExport-success'
} );
_this.baseRevId = claimData.pageinfo.lastrevid;
revIds.push( _this.baseRevId );
_this.createClaims( propertyId, values, refUrl, revIds );
} else {
_this.errorDialog( _this.i18n.saveFailed, JSON.stringify( claimData ) );
}
} );
};
/**
* Setting gadget tags for edits
* FIXME: Add tags directly for edit when [[phab:T155109]] will be fixed
*/
this.addTags = function ( propertyId, revIds ) {
_this.wdApi.postWithToken( 'csrf', {
action: 'tag',
add: 'InfoboxExport gadget',
revid: revIds
} ).done( function ( data ) {
var success = false;
if ( data.tag ) {
success = true;
for ( var i = 0; i < data.tag.length; i++ ) {
if ( data.tag[ i ].status !== 'success' ) {
success = false;
break;
}
}
}
if ( success ) {
mw.notify( _this.i18n.tagsSaved, {
tag: 'wikidataInfoboxExport-tags-success'
} );
$( '.no-wikidata[data-wikidata-property-id=' + propertyId + ']' )
.removeClass( 'no-wikidata' )
.off( 'dblclick', _this.clickEvent );
} else {
mw.notify( _this.i18n.tagsFailed, {
type: 'warn',
tag: 'wikidataInfoboxExport-tags-error'
} );
}
} );
};
/**
* Wrapper for property preloading that excludes already loaded properties
*/
this.loadProperties = function ( propertyIds ) {
if ( !propertyIds || !propertyIds.length ) {
return;
}
var realPropertyIds = [];
for ( var i in propertyIds ) {
var propertyId = propertyIds[ i ];
if ( propertyId && _this.config.properties[ propertyId ] === undefined ) {
realPropertyIds.push( propertyId );
}
}
if ( realPropertyIds.length ) {
_this.realLoadProperties( realPropertyIds );
}
};
/**
* Preload information on all properties
*/
this.realLoadProperties = function ( propertyIds ) {
if ( !propertyIds || !propertyIds.length ) {
return;
}
var units = [];
_this.wdApi.get( {
action: 'wbgetentities',
languages: _this.config.languages,
props: [ 'labels', 'datatype', 'claims' ],
ids: propertyIds
} ).done( function ( data ) {
if ( !data.success ) {
return;
}
for ( var propertyId in data.entities ) {
if ( !data.entities.hasOwnProperty( propertyId ) ) {
continue;
}
var entity = data.entities[ propertyId ];
var label = entity.labels[ _this.config.language ] ? entity.labels[ _this.config.language ].value : entity.labels.en.value;
_this.config.properties[ propertyId ] = {
datatype: entity.datatype,
label: label.charAt( 0 ).toUpperCase() + label.slice( 1 ),
constraints: { qualifier: [] },
units: []
};
if ( propertyId === 'P1128' || propertyId === 'P2196' ) {
_this.config.properties[ propertyId ].constraints.integer = 1;
}
if ( entity.claims ) {
// Property restrictions
if ( entity.claims.P2302 ) {
for ( var i in entity.claims.P2302 ) {
var type = ( ( ( ( entity.claims.P2302[ i ] || {} ).mainsnak || {} ).datavalue || {} ).value || {} ).id;
switch ( type ) {
case 'Q19474404':
case 'Q21502410':
_this.config.properties[ propertyId ].constraints.unique = 1;
break;
case 'Q21510856': // Required
qualifiers = ( ( ( entity.claims.P2302[ i ] || {} ).qualifiers || {} ).P2306 || [] );
for ( var idx = 0; idx < qualifiers.length; idx++) {
var qualifierId = ( ( ( qualifiers[ idx ] || {}).datavalue || {} ).value || {} ).id;
if ( qualifierId ) {
_this.config.properties[ propertyId ].constraints.qualifier.push( qualifierId.toString() );
}
}
break;
case 'Q21514353': // Units
qualifiers = ( ( ( entity.claims.P2302[ i ] || {} ).qualifiers || {} ).P2305 || [] );
for ( var idx = 0; idx < qualifiers.length; idx++) {
var unitId = ( ( ( qualifiers[ idx ] || {}).datavalue || {} ).value || {} ).id;
if ( unitId ) {
_this.config.properties[ propertyId ].units.push( unitId );
units.push( unitId );
}
}
break;
}
}
}
}
}
for ( var idx = 0; idx < $.uniqueSort( units ).length; idx += 50) {
_this.wdApi.get( {
action: 'wbgetentities',
languages: _this.config.languages,
props: [ 'labels', 'descriptions', 'aliases', 'claims' ],
ids: $.uniqueSort( units ).slice( idx, idx + 50 )
} ).done( function ( unitData ) {
if ( !unitData.success ) {
return;
}
for ( var unitId in unitData.entities ) {
var unit = unitData.entities[ unitId ];
var unitSearch = _this.config.units[ unitId ] ? _this.config.units[ unitId ].search : [];
if ( !_this.config.units[ unitId ] ) {
_this.config.units[ unitId ] = {};
}
// Label
if ( unit.labels ) {
_this.config.units[ unitId ].label = unit.labels[ _this.config.userLanguage ] ||
unit.labels.en ||
unit.labels[ Object.keys( unit.labels )[ 0 ] ];
if ( unit.labels[ _this.config.userLanguage ] ) {
unitSearch.push( unit.labels[ _this.config.userLanguage ].value.replace( /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&' ) );
}
}
// Description
if ( unit.descriptions ) {
_this.config.units[ unitId ].description = unit.descriptions[ _this.config.userLanguage ] ||
unit.descriptions.en ||
unit.descriptions[ Object.keys( unit.labels )[ 0 ] ];
}
// Aliases
if ( unit.aliases && unit.aliases[ _this.config.userLanguage ] ) {
for ( var i in unit.aliases[ _this.config.userLanguage ] ) {
unitSearch.push( unit.aliases[ _this.config.userLanguage ][ i ].value.replace( /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&' ) );
}
}
// Units (P558)
if ( unit.claims && unit.claims.P558 ) {
for ( var i in unit.claims.P558 ) {
var claim = unit.claims.P558[ i ];
if ( claim.mainsnak &&
claim.mainsnak.datavalue &&
claim.mainsnak.datavalue.value
) {
unitSearch.push( claim.mainsnak.datavalue.value.replace( /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&' ) );
}
}
}
_this.config.units[ unitId ].search = $.uniqueSort( unitSearch );
_this.saveConfig();
}
} );
}
} );
};
this.checkForMisLang = function ( wd ) {
var lang = wd.value.language;
if ( 'misLang' in _this.config && lang in _this.config.misLang ) {
wd.value.language = 'mis';
if ( !( 'qualifiers' in wd ) ) {
wd.qualifiers = {};
}
wd.qualifiers.P585 = [ {
property: 'P407',
snaktype: 'value',
datavalue: {
type: 'wikibase-entityid',
value: { id: _this.config.misLang[ lang ] }
}
} ];
}
return wd;
};
this.addQualifiers = function ( $field, value, $label, callback ) {
var $qualifiers = $field.find( '[data-wikidata-qualifier-id]' );
if ( $qualifiers.length ) {
$label = $( '<div>' ).append( $label );
}
var addQualifierValue = function ( qualifierId, qualifierValue, qualifierLabel ) {
if ( value.qualifiers === undefined ) {
value.qualifiers = {};
}
if ( value.qualifiers[ qualifierId ] === undefined ) {
value.qualifiers[ qualifierId ] = [];
}
var datatype = _this.config.properties[ qualifierId ].datatype;
value.qualifiers[ qualifierId ].push( {
snaktype: 'value',
property: qualifierId,
datavalue: {
type: _this.typesMapping[ datatype ] ? _this.typesMapping[ datatype ] : datatype,
value: qualifierValue
}
} );
$label.append( $( '<p>' )
.append( $( '<a>' )
.attr( 'href', '//www.wikidata.org/wiki/Property:' + qualifierId )
.text( _this.config.properties[ qualifierId ].label )
)
.append( $( '<span>' ).text( ': ' ) )
.append( qualifierLabel )
);
};
var qualifierTitles = {};
for ( var q = 0; q < $qualifiers.length; q++ ) {
var $qualifier = $( $qualifiers[ q ] );
var qualifierId = $qualifier.data( 'wikidata-qualifier-id' );
var qualifierValue = $qualifier.text().replace( '\n', ' ' ).trim();
switch ( _this.config.properties[ qualifierId ].datatype ) {
case 'monolingualtext':
qualifierValue = {
text: $qualifier.text().replace( '\n', ' ' ).trim(),
language: $qualifier.attr( 'lang' ) || _this.config.language
};
addQualifierValue( qualifierId, qualifierValue, $qualifier.text() );
break;
case 'string':
qualifierValue = $qualifier.text().replace( '\n', ' ' ).trim();
addQualifierValue( qualifierId, qualifierValue, $qualifier.text() );
break;
case 'time':
qualifierValue = _this.createTimeSnak( qualifierValue );
addQualifierValue( qualifierId, qualifierValue, $qualifier.text() );
break;
case 'wikibase-item':
if ( qualifierTitles[ qualifierId ] === undefined ) {
qualifierTitles[ qualifierId ] = [];
}
var $links = $qualifier.find( 'a[title][class!=image][class!=new]' );
if ( $links.length ) {
for ( var l = 0; l < $links.length; l++ ) {
var $link = $( $links[ l ] );
var extractedUrl = decodeURIComponent( $link.attr( 'href' ) ).replace( /^.*\/wiki\//, '' );
if ( extractedUrl ) {
extractedUrl = extractedUrl.replace( /_/g, ' ' ).trim();
var title = {
label: extractedUrl.charAt( 0 ).toUpperCase() + extractedUrl.substr( 1, extractedUrl.length - 1 ),
language: _this.config.contentLanguage,
project: _this.config.project,
qualifiers: {}
};
if ( $link.hasClass( 'extiw' ) ) {
var m = $link.attr( 'href' ).match( /^https:\/\/([a-z\-]+)\.(wik[^\.]+)\./ );
if ( m && m[ 2 ] !== 'wikimedia' ) {
title.language = m[ 1 ];
title.project = m[ 1 ] + m[ 2 ].replace( 'wikipedia', 'wiki' );
}
}
qualifierTitles[ qualifierId ].push( title );
}
}
} else {
qualifierTitles[ qualifierId ].push( {
label: qualifierValue.charAt( 0 ).toUpperCase() + qualifierValue.substr( 1, qualifierValue.length - 1 ),
language: _this.config.contentLanguage,
project: _this.config.project,
qualifiers: {}
} );
}
break;
}
}
var processItemTitles = function ( itemTitles, callback ) {
if ( Object.keys( itemTitles ).length ) {
var qualifierId = Object.keys( itemTitles ).shift();
var qualifierItemTitles = itemTitles[ qualifierId ];
delete itemTitles[ qualifierId ];
_this.getWikidataIds( qualifierItemTitles, function ( valuesObj ) {
for ( var entityId in valuesObj ) {
var valueObj = valuesObj[ entityId ];
addQualifierValue( qualifierId, valueObj.wd.value, valueObj.label );
}
processItemTitles( itemTitles, callback );
} );
} else {
callback( {
wd: value,
label: $label
} );
}
};
processItemTitles( qualifierTitles, callback );
};
/**
* Parsing values from parameters before displaying a dialog
*/
this.prepareDialog = function ( $field, propertyId ) {
var values = [];
var datatype = _this.config.properties[ propertyId ].datatype;
var $content = $field.clone();
$content.find( 'sup.reference' ).remove();
$content.find( '[style*="display:none"]' ).remove();
var $wrapper = $content;
var $row = $field.closest( 'tr' );
if ( $row.length === 1 && $row.find( '[data-wikidata-property-id]' ).length === 1 ) {
$wrapper = $row.clone();
}
switch ( datatype ) {
case 'commonsMedia':
var $imgs = $content.find( 'img' );
$imgs.each( function () {
var $img = $( this );
var src = $img.attr( 'src' );
if ( !src.match( /upload.wikimedia.org\/wikipedia\/commons/ ) ) {
return;
}
var srcParts = src.split( '/' );
var fileName = srcParts.pop();
if ( fileName.match( /(?:^|-)\d+px-/ ) ) {
fileName = srcParts.pop();
}
fileName = decodeURIComponent( fileName );
fileName = fileName.replace( /_/g, ' ' );
var value = { value: fileName };
var $label = $img.clone()
.attr( 'title', fileName )
.css( 'border', '1px dashed #a2a9b1' );
_this.addQualifiers( $wrapper, value, $label, function( valueObj ) {
values.push( valueObj );
} );
} );
break;
case 'external-id':
var externalId = $content.data( 'wikidata-external-id' ) || $content.text();
if ( propertyId === 'P345' ) { // IMDB
externalId = $content.find( 'a' ).first().attr( 'href' );
externalId = externalId.substr( externalId.lastIndexOf( '/', externalId.length - 2 ) ).replace( /\//g, '' );
} else {
externalId = externalId.toString().replace( /^ID\s/, '' ).replace( /\s/g, '' );
}
var sparql = 'SELECT * WHERE { ?item wdt:' + propertyId + ' "' + externalId + '" }';
$.ajax( {
url: 'https://query.wikidata.org/sparql?format=json&query=' + sparql,
success: function ( data ) {
var $label = $( '<code>' ).text( externalId );
if ( data.results.bindings.length ) {
var url = data.results.bindings[ 0 ].item.value;
$label = $( '<span>' ).append( $( '<code>' ).text( externalId ) )
.append( $( '<strong>' ).css( { 'color': 'red' } ).text( _this.i18n.alreadyUsedIn ) )
.append( $( '<a>' ).attr( 'href', url ).attr( 'target', '_blank' ).text( url.replace( /[^Q]*Q/, 'Q' ) ) );
}
_this.dialog( $field, propertyId, [ {
wd: { value: externalId.toString() },
label: $label
} ], _this.getReference( $content ) );
}
} );
return;
case 'string':
var text = $content.data( 'wikidata-external-id' );
if ( !text ) {
text = $content.text();
}
var strings = text.toString().trim().split( /[\n,;]+/ );
// Commons category
if ( propertyId === 'P373' ) {
var $link = $content.find( 'a[class="extiw"]' ).first();
if ( $link.length ) {
var url = $link.attr( 'href' );
var value = url.substr( url.lastIndexOf( '/' ) + 1 )
.replace( /_/g, ' ' )
.replace( /^[Cc]ategory:/, '' )
.replace( /\?.*$/, '' );
value = decodeURIComponent( value );
strings = [ value ];
}
}
for ( var i in strings ) {
var s = strings[ i ].replace( /\n/g, ' ' ).trim();
if ( s ) {
values.push( {
wd: { value: s },
label: $( '<code>' + s + '</code>' )
} );
}
}
break;
case 'monolingualtext':
var $items = $content.find( 'span[lang]' );
$items.each( function () {
var $item = $( this );
values.push( {
wd: _this.checkForMisLang( {
value: {
text: $item.text().trim(),
language: $item.attr( 'lang' ).trim()
}
} )
} );
} );
if ( !values.length ) {
var text = $content.text().trim();
if ( text ) {
var $items = mw.util.$content.find( 'span[lang]' );
$items.each( function () {
$item = $( this );
if ( $item.text().trim().startsWith( text ) ) {
values.push( {
wd: {
value: {
text: text,
language: $item.attr( 'lang' ).trim()
}
}
} );
}
} );
}
}
var valueLanguages = [];
for ( var i in values ) {
var valueLanguage = values[ i ].wd.value.language;
if ( valueLanguages.indexOf( valueLanguage ) > -1 ) {
continue;
}
valueLanguages.push( valueLanguage );
values[ i ].label = $( '<span>' )
.append( $( '<span>' ).css( 'color', '#666' ).text( '(' + valueLanguage + ') ' ) )
.append( $( '<strong>' ).text( values[ i ].wd.value.text ) );
}
values = values.filter( function( item ) {
return item.label !== undefined;
} );
break;
case 'quantity':
var text = $content.text()
.replace( /[\u00a0\u25bc\u25b2]/g, ' ' )
.replace( /\s*\(([^)]*\))/g, '' )
.trim();
// Hack for time in formats "hh:mm:ss" and "00m 00s""
var match = text.replace( _this.config.reMinSec, '$1:$2' )
.match( /^(?:(\d+):)?(\d+):(\d+)$/ );
if ( match ) {
var amount = 0;
for ( var i = 1; i < match.length; i++ ) {
if ( match[ i ] !== undefined ) {
amount = amount * 60 + parseInt( match[ i ], 10 );
}
}
text = amount + _this.i18n.unitSec;
}
var result = { wd: _this.parseQuantity( text, _this.config.properties[ propertyId ].constraints.integer ) };
if ( !result.wd || !result.wd.value ) {
break;
}
_this.addQualifiers( $wrapper, result.wd, _this.formatDataValue( result.wd ), function( valueObj ) {
result = valueObj;
} );
if ( _this.config.properties[ propertyId ].constraints.qualifier.indexOf( 'P585' ) !== -1 ) {
var yearMatch = $content.text().match( /\(([^)]*[12]\s?\d\d\d)[,)\s]/ );
if ( !yearMatch ) {
yearMatch = $field.closest( 'tr' ).find( 'th' ).first().text().match( /\(([^)]*[12]\s?\d\d\d)[,)\s]/ );
}
if ( yearMatch ) {
if ( extractedDate = _this.createTimeSnak( yearMatch[ 1 ].replace ( /(\d)\s(\d)/, '$1$2' ) ) ) {
result.wd.qualifiers = {
P585: [ {
snaktype: 'value',
property: 'P585',
datavalue: {
type: 'time',
value: extractedDate
}
} ]
};
}
}
}
var qualMatch = $content.text().match( /\(([^\)]*)/ );
if ( qualMatch ) {
qualQuantity = _this.parseQuantity( qualMatch[ 1 ] );
if ( qualQuantity ) {
var supportedProperties = [ 'P2076', 'P2077' ];
for ( var j = 0; j < supportedProperties.length; j++ ) {
var units = _this.recognizeUnits( qualMatch[ 1 ], _this.config.properties[ supportedProperties[ j ] ].units );
if ( units.length === 1 ) {
qualQuantity.value.unit = 'http://www.wikidata.org/entity/' + units[ 0 ];
if ( !result.wd.qualifiers ) {
result.wd.qualifiers = {};
}
result.wd.qualifiers[ supportedProperties[ j ] ] = [ {
snaktype: 'value',
property: supportedProperties[ j ],
datavalue: {
type: 'quantity',
value: qualQuantity.value
}
} ];
}
}
}
}
var founded = _this.recognizeUnits( text, _this.config.properties[ propertyId ].units, $field.closest( 'tr' ).find( 'th' ).first().text() );
for ( var u = 0; u < founded.length; u++ ) {
result.wd.value.unit = '1';
if ( founded[ u ] !== '1' ) {
result.wd.value.unit = 'http://www.wikidata.org/entity/' + founded[ u ];
var item = _this.config.units[ founded[ u ] ];
}
result.wd.type = 'quantity';
result.label = _this.formatDataValue( result.wd );
values.push( result );
}
break;
case 'time':
var value = _this.createTimeSnak( $content.text().toLowerCase().trim().replace( _this.config.reYearPostfix, '' ),
$content[ 0 ].outerHTML.includes( _this.config.markJulian ) );
if ( value ) {
if ( value.toString().match( /^(novalue|somevalue)$/ ) ) {
var $label = $( '<span>' );
if ( wueI18n.valuePrefix !== '' ) {
$label.append( $( '<span>' ).css( 'color', '#666' ).text( wueI18n.valuePrefix ) );
}
$label.append( $( '<strong>' ).text( value.toString() === 'novalue' ? wueI18n.noValue : wueI18n.unknownValue ) );
values.push( {
wd: { value: value },
label: $label
} );
} else {
values.push( {
wd: { value: value },
label: $( '<span>' )
.append( $( '<strong>' ).append( _this.formatDataValue( {
type: 'time',
value: value
} ) ) )
.append( $( '<span>' ).css( 'color', '#666' ).text( ' (' +
( value.calendarmodel.includes( '1985727' ) ? _this.i18n.grigorianCalendar : _this.i18n.julianCalendar ) + ') ' ) )
} );
}
}
break;
case 'wikibase-item':
value = _this.parseItems( $content, $wrapper, function ( values ) {
_this.dialog( $field, propertyId, values, _this.getReference( $content ) );
} );
return;
case 'url':
var $links = $content.find( 'a' );
$links.each( function () {
var $link = $( this );
var url = $link.attr( 'href' ).replace( /^\/\//, 'https://' );
values.push( {
wd: { value: url },
label: $( '<code>' + url + '</code>' )
} );
} );
break;
default:
mw.notify( _this.i18n.unknownDatatype.replace( '$1', datatype ), {
type: 'error',
tag: 'wikidataInfoboxExport-error'
} );
}
values = $.uniqueSort( values );
_this.dialog( $field, propertyId, values, _this.getReference( $field ) );
};
/**
* Double-click event on the infobox field
*/
this.clickEvent = function ( e ) {
var $field = $( this );
var propertyId = $field.attr( 'data-wikidata-property-id' );
_this.prepareDialog( $field, propertyId );
};
/**
* Display a dialog to confirm export
*/
this.dialog = function ( $field, propertyId, values, refUrl ) {
var fieldset;
if ( !values || !values.length ) {
mw.notify( _this.i18n.parsingError, {
type: 'error',
tag: 'wikidataInfoboxExport-error'
} );
return;
}
// Create a dialog
function ProcessDialog( config ) {
ProcessDialog.super.call( this, config );
}
OO.inheritClass( ProcessDialog, OO.ui.ProcessDialog );
ProcessDialog.static.name = _this.i18n.windowHeader;
ProcessDialog.static.title = $( '<span>' )
.attr( 'title', _this.i18n.versionString.replace( '$1', _this.config.version ) )
.text( ProcessDialog.static.name );
ProcessDialog.static.actions = [
{ action: 'export', label: _this.i18n.exportButtonLabel, flags: [ 'primary', 'progressive' ] },
{ label: _this.i18n.cancelButtonLabel, flags: [ 'safe' ] }
];
ProcessDialog.prototype.initialize = function () {
ProcessDialog.super.prototype.initialize.apply( this, arguments );
this.content = new OO.ui.PanelLayout( { padded: true, expanded: false } );
fieldset = new OO.ui.FieldsetLayout();
var firstSelected = false;
for ( var i = 0; i < values.length; i++ ) {
var alreadyInWikidata = ( _this.alreadyExistingItems[ propertyId ] || [] ).includes( ( ( values[ i ].wd || {} ).value || {} ).id );
var checkbox = new OO.ui.CheckboxInputWidget( {
value: JSON.stringify( values[ i ].wd ),
selected: alreadyInWikidata,
disabled: alreadyInWikidata
} );
if ( !checkbox.isDisabled() ) {
if ( !firstSelected || !_this.config.properties[ propertyId ].constraints.unique ) {
firstSelected = true;
checkbox.setSelected( true );
}
if ( values[ i ].label[ 0 ].innerText.match( new RegExp( _this.i18n.alreadyUsedIn ) ) &&
_this.config.properties[ propertyId ].constraints.unique &&
_this.config.properties[ propertyId ].datatype === 'external-id' ) {
checkbox.setSelected( false );
}
}
if ( refUrl ) {
values[ i ].label.append( _this.formatDomains( refUrl ) );
}
fieldset.addItems( [
new OO.ui.FieldLayout( checkbox, {
label: values[ i ].label,
align: 'inline'
} )
] );
}
this.content.$element
.append( $( '<p>' ).append( $( '<strong>' )
.append( $( '<a>' ).attr( 'href', 'https://wikidata.org/wiki/Property:' + propertyId ).attr( 'target', '_blank' ).text( _this.config.properties[ propertyId ].label ) )
.append( $( '<span>' ).text( ':' ) )
) )
.append( fieldset.$element )
.append( $( '<hr>' ).css( 'margin-top', '1.5em' ) )
.append( $( '<p>' ).text( _this.i18n.exportConfirmation ) )
.append( $( '<p>' ).css( 'font-size', 'smaller' ).html( _this.i18n.licenseCc0 ) );
this.$body.append( this.content.$element );
};
ProcessDialog.prototype.getActionProcess = function ( action ) {
var dialog = this;
if ( action === 'export' ) {
return new OO.ui.Process( function () {
var values = [];
var fields = fieldset.getItems();
for ( var i in fields ) {
var checkbox = fields[ i ].getField();
if ( checkbox.isSelected() && !checkbox.isDisabled() ) {
values.push( checkbox.getValue() );
}
}
_this.createClaims( propertyId, values, refUrl );
dialog.close( { action: action } );
}, this );
}
return ProcessDialog.super.prototype.getActionProcess.call( this, action );
};
var windowManager = new OO.ui.WindowManager();
$( 'body' ).append( windowManager.$element );
var processDialog = new ProcessDialog();
windowManager.addWindows( [ processDialog ] );
windowManager.openWindow( processDialog );
};
/**
* Initializing the gadget
*/
this.init = function () {
if ( mw.config.get( 'wgWikibaseItemId' ) === null ||
mw.config.get( 'wgAction' ) !== 'view' ||
mw.util.getParamValue( 'veaction' ) !== null ||
( window.ve && window.ve.init ) ||
mw.config.get( 'wgNamespaceNumber' )
) {
return;
}
_this.loadConfig();
var sparql = 'SELECT ?wiki WHERE { ?wiki wdt:P31/wdt:P279* wd:Q33120876 . ?wiki wdt:P856 ?site . FILTER REGEX(STR(?site), "https://' + location.host + '/") }';
$.ajax( {
url: 'https://query.wikidata.org/sparql?format=json&query=' + sparql,
success: function ( data ) {
if ( 0 === data.results.bindings.length ) {
return;
}
// Add current wiki project as "imported from Wikimedia project"
var projectId = data.results.bindings[ 0 ].wiki.value.replace( 'http://www.wikidata.org/entity/', '' );
_this.config.references.P143 = [ {
property: 'P143',
snaktype: 'value',
datavalue: {
type: 'wikibase-entityid',
value: { id: projectId }
}
} ];
_this.initContinue();
}
} );
};
/**
* Save config to localStorage
*/
this.saveConfig = function () {
var config = _this.config;
for ( var key in config ) {
var value = config[ key ];
if ( value instanceof RegExp ) {
config[ key ] = value.source;
}
}
localStorage.setItem( config.storageKey, JSON.stringify( config ) );
};
/**
* Load config from localStorage
*/
this.loadConfig = function () {
var config;
try {
config = JSON.parse( localStorage.getItem( _this.config.storageKey ) );
} catch ( e ) {}
for ( var key in config ) {
if ( key.match( /^re[A-Z1]/ ) && typeof config[ key ] === 'string' ) {
config[ key ] = new RegExp( config[ key ] );
}
}
if ( config &&
config.version === _this.config.version &&
config.userLanguage === _this.config.userLanguage
) {
_this.config = config;
}
if ( _this.config.properties === undefined ) {
_this.config.properties = {};
}
};
/**
* Load config from localStorage
*/
this.loadCommonsConfig = function () {
_this.commonsApi.get( {
action: 'jsondata',
formatversion: 2,
title: 'I18n/WikidataInfoboxExportConfig.tab',
uselang: _this.config.contentLanguage
} ).done( function ( data ) {
if ( !data.jsondata || !data.jsondata.data ) {
return;
}
for ( var i in data.jsondata.data ) {
var row = data.jsondata.data[ i ];
var key = row[ 0 ].replace(/-([a-z1])/g, function ( g ) {
return g[ 1 ].toUpperCase();
});
var value = row[ 1 ];
if ( key.match( /^re[A-Z1]/ ) ) {
value = value.replace( '%months%', _this.months.join( '|' ) );
value = value.replace( '%months-gen%', _this.monthsGen.join( '|' ) );
if ( value === '' ) {
value = '^@{999}$'; // impossible regexp
}
value = new RegExp( value );
}
_this.config[ key ] = value;
}
_this.saveConfig();
} );
var config;
try {
config = JSON.parse( localStorage.getItem( _this.config.storageKey ) );
} catch ( e ) {}
for ( var key in config ) {
if ( key.match( /^re[A-Z1]/ ) && typeof config[ key ] === 'string' ) {
config[ key ] = new RegExp( config[ key ] );
}
}
if ( config && config.version == _this.config.version ) {
_this.config = config;
}
if ( _this.config.properties === undefined ) {
_this.config.properties = {};
}
};
/**
* Load internationalization data from Commons
*/
this.loadI18n = function () {
_this.commonsApi.get( {
action: 'jsondata',
formatversion: 2,
title: 'I18n/WikidataInfoboxExport.tab',
uselang: _this.config.userLanguage
} ).done( function ( data ) {
if ( !data.jsondata || !data.jsondata.data ) {
return;
}
for ( var i in data.jsondata.data ) {
var row = data.jsondata.data[ i ];
var key = row[ 0 ].replace(/-([a-z1])/g, function ( g ) {
return g[ 1 ].toUpperCase();
});
_this.i18n[ key ] = row[ 1 ];
}
_this.i18n.licenseCc0 = _this.i18n.licenseCc0
.replace( '$button', this.i18n.exportButtonLabel )
.replace( '$terms', 'href="https://foundation.wikimedia.org/wiki/Terms_of_Use" class="extiw" title="wikimedia:Terms of Use"' )
.replace( '$license', 'rel="nofollow" class="external text" href="https://creativecommons.org/publicdomain/zero/1.0/"' );
} );
};
/**
* Load local month names from messages API
*/
this.loadMonths = function () {
var messageKeys = [];
for ( var i in _this.months ) {
messageKeys.push( _this.months[ i ] );
messageKeys.push( _this.months[ i ] + '-gen' );
}
_this.api.getMessages( messageKeys, { amlang: _this.config.contentLanguage } )
.then( function ( messages ) {
var monthLocal = [];
var monthLocalGen = [];
for ( var pos in _this.months ) {
var key = _this.months[ pos ];
monthLocal.push( messages[ key ] );
monthLocalGen.push( messages[ key + '-gen' ] );
}
_this.months = monthLocal;
_this.monthsGen = monthLocalGen;
} );
};
/**
* Continue gadget initializing
*/
this.initContinue = function () {
// Add a link to the current version of the page as "Wikimedia import URL"
_this.config.references.P4656 = [ {
property: 'P4656',
datatype: 'url',
snaktype: 'value',
datavalue: {
type: 'string',
value: 'https://' + location.host + '/?oldid=' + mw.config.get( 'wgRevisionId' )
}
} ];
_this.saveConfig();
// API initialization
_this.api = new mw.Api();
_this.wdApi = new mw.ForeignApi( '//www.wikidata.org/w/api.php' );
_this.commonsApi = new mw.ForeignApi( '//commons.wikimedia.org/w/api.php' );
// Dialogs initialization
_this.windowManager = new OO.ui.WindowManager();
$( 'body' ).append( _this.windowManager.$element );
_this.loadI18n();
_this.loadMonths();
_this.loadCommonsConfig();
// Item data request
_this.wdApi.get( {
action: 'wbgetentities',
props: [ 'info', 'claims' ],
ids: mw.config.get( 'wgWikibaseItemId' )
} ).done( function ( data ) {
if ( data.success ) {
var claims;
for ( var i in data.entities ) {
if ( i == -1 ) {
return;
}
claims = data.entities[ i ].claims;
_this.baseRevId = data.entities[ i ].lastrevid;
break;
}
if ( !claims ) {
return;
}
var $fields = $( '.infobox .no-wikidata' );
$fields.each( function () {
var $field = $( this );
var propertyId = $field.attr( 'data-wikidata-property-id' );
$field
.removeClass( 'no-wikidata' )
.off( 'dblclick' );
_this.propertyIds.push( propertyId );
_this.canExportValue( $field, claims[ propertyId ], function ( hasClaims ) {
$field.addClass( 'no-wikidata' );
if ( hasClaims === true ) {
$field.addClass( 'partial-wikidata' );
}
$field.on( 'dblclick', _this.clickEvent );
} );
var $fieldQualifiers = $field.closest( 'tr' ).find( '[data-wikidata-qualifier-id]' );
$fieldQualifiers.each( function () {
_this.propertyIds.push( $( this ).data( 'wikidata-qualifier-id' ) );
} );
} );
mw.util.addCSS( '\
.infobox .no-wikidata {\
display: block !important;\
background: #fdc;\
padding: 5px 0;\
}\
.infobox .no-wikidata.partial-wikidata {\
background: #eeb;\
}\
.infobox .no-wikidata .no-wikidata {\
margin: -5px 0;\
}\
' );
// TODO: Do not load properties until the window is opened for the first time
_this.loadProperties( _this.propertyIds );
}
} );
};
return this;
};
$.when(
$.ready,
mw.loader.using( [
'mediawiki.api',
'mediawiki.ForeignApi',
'mediawiki.util',
'oojs-ui-core',
'oojs-ui-widgets',
'oojs-ui-windows'
] )
)
.done( wikidataInfoboxExport().init );
}( mediaWiki, jQuery ) );