MediaWiki:Gadget-CiteTool.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)
- Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5.
/**
* This is a tool to help with adding information when using URLs as references.
*
* The tool adds a button when adding/editing a reference which has a reference URL (P854).
* When the button is clicked, it queries Citoid for the entered URL and
* adds any extra reference fields (e.g. title, language) that it finds.
*
* This is a fork of [[User:MichaelSchoenitzer/CiteTool.js]],
* which itself is a fork of [[User:Aude/CiteTool.js]],
* and incorporates changes from [[phab:T238479#6384675]]
* and [[User:Samwilson/CiteTool.js]].
*/
( function( wb, dv, mw, $ ) {
'use strict';
function CiteTool( configUrl ) {
this.configUrl = configUrl;
this.config = null;
this.citoidClient = new CitoidClient();
this.citeToolReferenceEditor = null;
this.citeToolAutofillLinkRenderer = null;
}
CiteTool.prototype.init = function() {
var self = this;
if ( !mw.config.exists( 'wbEntityId' ) ) {
return;
}
var translations = require( './CiteTool-i18n.json' );
$.i18n().load( translations );
$( '.wikibase-entityview' )
.on( 'referenceviewafterstartediting', function( e ) {
self.initAutofillLink( e.target );
} );
// @fixme the event also fires for other changes, like editing qualifiers
$( '.wikibase-entityview' )
.on( 'snakviewchange', function( e ) {
self.initAutofillLink( e.target );
} );
};
CiteTool.prototype.getConfig = function() {
var dfd = $.Deferred();
if ( this.configUrl ) {
$.ajax( {
url: this.configUrl,
dataType: 'json',
success: function( config ) {
dfd.resolve( config );
},
error: function( result ) {
console.error( 'Error loading citoid config from ' + this.configUrl );
}
} );
} else {
var config = require( './CiteTool-config.json' );
dfd.resolve( config );
}
return dfd.promise();
};
CiteTool.prototype.initAutofillLink = function( target ) {
var self = this;
if ( this.config === null ) {
this.getConfig()
.done( function( config ) {
self.config = config;
self.citeToolReferenceEditor = new CiteToolReferenceEditor( config );
self.citeToolAutofillLinkRenderer = new CiteToolAutofillLinkRenderer(
config,
self.citoidClient,
self.citeToolReferenceEditor
);
self.checkReferenceAndAddAutofillLink( target );
} );
} else {
var $refViews = $( target ).closest( '.wikibase-referenceview' );
self.checkReferenceAndAddAutofillLink( $refViews[0] );
}
};
CiteTool.prototype.checkReferenceAndAddAutofillLink = function( target ) {
if ( $( target ).find( '.wikibase-citetool-autofill' ).length > 0 ) {
return;
}
var reference = this.getReferenceFromView( target );
if ( reference && this.getLookupSnakProperty( reference ) !== null ) {
this.citeToolAutofillLinkRenderer.renderLink( target );
}
};
CiteTool.prototype.getReferenceFromView = function( referenceView ) {
// not a reference view change
if ( referenceView === undefined ) {
return null;
}
var refView = $( referenceView ).data( 'referenceview' );
return refView && refView.value && refView.value();
};
CiteTool.prototype.getLookupSnakProperty = function( reference ) {
var snaks = reference.getSnaks(),
lookupProperties = this.getLookupProperties(),
lookupProperty = null;
snaks.each( function( k, snak ) {
var propertyId = snak.getPropertyId();
if ( lookupProperties.indexOf( propertyId ) !== -1 ) {
if ( lookupProperty === null ) {
lookupProperty = propertyId;
}
}
} );
return lookupProperty;
};
CiteTool.prototype.getLookupProperties = function() {
var properties = [];
if ( this.config.properties ) {
properties = Object.keys( this.config.properties );
}
return properties;
};
function CiteToolAutofillLinkRenderer( config, citoidClient, citeToolReferenceEditor ) {
this.config = config;
this.citoidClient = citoidClient;
this.citeToolReferenceEditor = citeToolReferenceEditor;
}
CiteToolAutofillLinkRenderer.prototype.renderLink = function( referenceView ) {
var self = this;
var $div = $( '<div>' )
.addClass( 'wikibase-toolbar-button wikibase-citetool-autofill' )
.append(
$( '<a>' )
.text( $.i18n( 'gadget-citetool-btn' ) )
.attr( {
'class': 'wikibase-referenceview-autofill',
'tabindex': '0',
'title': $.i18n( 'gadget-citetool-btn-title' )
} )
.on( 'click', function( e ) {
e.preventDefault();
self.onAutofillClick( e.target );
} )
.on( 'keydown', function( e ) {
if ( e.keyCode === 13 ) { // enter
e.preventDefault();
self.onAutofillClick( e.target );
}
} )
);
$( referenceView ).append( $div );
};
CiteToolAutofillLinkRenderer.prototype.getReferenceFromView = function( $referenceView ) {
// not a reference view change
if ( $referenceView === undefined ) {
return null;
}
var refView = $referenceView.data( 'referenceview' );
return refView.value();
};
CiteToolAutofillLinkRenderer.prototype.getLookupSnakProperty = function( reference ) {
var snaks = reference.getSnaks(),
lookupProperties = this.getLookupProperties(),
lookupProperty = null;
snaks.each( function( k, snak ) {
var propertyId = snak.getPropertyId();
if ( lookupProperties.indexOf( propertyId ) !== -1 ) {
if ( lookupProperty === null ) {
lookupProperty = propertyId;
}
}
} );
return lookupProperty;
};
CiteToolAutofillLinkRenderer.prototype.getLookupProperties = function() {
var properties = [];
if ( this.config.properties ) {
properties = Object.keys( this.config.properties );
}
return properties;
};
CiteToolAutofillLinkRenderer.prototype.onAutofillClick = function( target ) {
var $referenceView = $( target ).closest( '.wikibase-referenceview' ),
reference = this.getReferenceFromView( $referenceView ),
self = this;
if ( reference === null ) {
return;
}
var value = this.getLookupSnakValue( reference );
var progressbar = new OO.ui.ProgressBarWidget( {
progress: false
} );
$referenceView.append( progressbar.$element );
this.citoidClient.search( value )
.done( function( data ) {
progressbar.$element.remove();
if ( data[ 0 ] ) {
self.citeToolReferenceEditor.addReferenceSnaksFromCitoidData(
data[ 0 ],
$referenceView
);
}
} );
};
CiteToolAutofillLinkRenderer.prototype.getLookupSnakValue = function ( reference ) {
var value = null,
lookupProperties = this.getLookupProperties();
reference.getSnaks().each( function( k, snak ) {
var propertyId = snak.getPropertyId();
if ( lookupProperties.indexOf( propertyId ) !== -1 ) {
value = snak.getValue().getValue();
}
} );
return value;
};
function CiteToolReferenceEditor( config ) {
this.config = config;
this.citoidClient = new CitoidClient();
}
CiteToolReferenceEditor.prototype.addReferenceSnaksFromCitoidData = function( data, $referenceView ) {
var refView = $referenceView.data( 'referenceview' ),
lv = this.getReferenceSnakListView( refView ),
usedProperties = refView.value().getSnaks().getPropertyOrder(),
self = this;
var addedSnakItem = false;
$.each( data, function( key, val ) {
var property = self.getPropertyForCitoidData( key );
if ( property === null ) {
return;
}
var propertyId = property.id;
var propertyType = property.valuetype;
if ( propertyId !== null && usedProperties.indexOf( propertyId ) !== -1 ) {
return;
}
if ( key === 'language' ) {
val = self.getLanguageItem( data );
}
if ( !val ) {
return;
}
if ( key === 'ISSN' ) {
for ( var i = 0; i < val.length; i++ ) {
lv.addItem( self.getStringValueSnak( 'P236', val[ i ] ) );
addedSnakItem = true;
}
return;
}
switch ( propertyType ) {
case 'item':
lv.addItem( self.getItemValueSnak( propertyId, val ) );
addedSnakItem = true;
break;
case 'monolingualtext':
val = val.replace( /\s+/g, ' ' );
lv.addItem( self.getMonolingualValueSnak(
propertyId,
val,
self.getTitleLanguage( data )
) );
if ( !self.getLanguage( data ) ) {
mw.notify( $.i18n( 'gadget-citetool-error-language' ), {
title: $.i18n( 'gadget-citetool-dialog-title' ),
type: 'warn',
autohide: true
} );
}
addedSnakItem = true;
break;
case 'string':
val = val.replace( /\s+/g, ' ' );
lv.addItem( self.getStringValueSnak(
propertyId,
val,
data
) );
addedSnakItem = true;
break;
case 'date':
try {
lv.addItem( self.getDateSnak( propertyId, val ) );
addedSnakItem = true;
} catch ( e ) { }
break;
case 'author':
for ( var i = 0; i < val.length; i++ ) {
var authorName = val[ i ]
.map( function ( a ) {
return a.trim();
} ).join( ' ' );
lv.addItem( self.getStringValueSnak( propertyId, authorName ) );
addedSnakItem = true;
}
break;
default:
break;
}
} );
if ( addedSnakItem === true ) {
lv.startEditing();
refView._trigger( 'change' );
}
};
CiteToolReferenceEditor.prototype.getReferenceSnakListView = function( refView ) {
var refListView = refView.$listview.data( 'listview' ),
snakListView = refListView.items(),
snakListViewData = snakListView.data( 'snaklistview' ),
listView = snakListViewData.$listview.data( 'listview' );
return listView;
};
CiteToolReferenceEditor.prototype.getPropertyForCitoidData = function( key ) {
if ( this.config.zoteroProperties[key] ) {
return this.config.zoteroProperties[key];
}
return null;
};
CiteToolReferenceEditor.prototype.getLanguage = function( data ) {
if ( data.language ) {
var languageCode = data.language.toLowerCase();
if ( languageCode in mw.config.values.wgULSLanguages ) {
return languageCode;
}
languageCode = languageCode.split( '-' )[ 0 ];
if ( languageCode in mw.config.values.wgULSLanguages ) {
return languageCode;
}
}
return null;
};
CiteToolReferenceEditor.prototype.getTitleLanguage = function( data ) {
var lang = this.getLanguage( data );
if ( lang ) {
return lang;
}
return 'en';
};
CiteToolReferenceEditor.prototype.getLanguageItem = function( data ) {
var langcode = this.getLanguage( data );
if ( langcode && this.config.languages[ langcode ] ) {
return this.config.languages[ langcode ];
}
return null;
};
CiteToolReferenceEditor.prototype.getMonolingualValueSnak = function( propertyId, title, languageCode ) {
return new wb.datamodel.PropertyValueSnak(
propertyId,
new dv.MonolingualTextValue( languageCode, title.trim() )
);
};
CiteToolReferenceEditor.prototype.getStringValueSnak = function( propertyId, string ) {
return new wb.datamodel.PropertyValueSnak(
propertyId,
new dv.StringValue( string.trim() )
);
};
CiteToolReferenceEditor.prototype.getItemValueSnak = function( propertyId, item ) {
return new wb.datamodel.PropertyValueSnak(
propertyId,
new wb.datamodel.EntityId( item )
);
};
CiteToolReferenceEditor.prototype.getDateSnak = function( propertyId, dateString ) {
var timestamp = dateString + 'T00:00:00Z';
return new wb.datamodel.PropertyValueSnak(
propertyId,
new dv.TimeValue( timestamp )
);
};
/**
* Client for fetching data from the Citoid API.
* @class
*/
function CitoidClient() {
}
/**
* @param {string} value Search term.
* @return {Promise}
*/
CitoidClient.prototype.search = function( value ) {
var encoded;
try {
encoded = encodeURIComponent( decodeURI( value ) );
} catch ( e ) {
encoded = encodeURIComponent( value );
}
var dfd = $.Deferred(),
baseUrl = 'https://en.wikipedia.org/api/rest_v1/data/citation',
format = 'mediawiki',
url = baseUrl + '/' + format + '/' + encoded;
$.ajax( {
method: 'GET',
timeout: 20 * 1000, // 20 seconds
url: url,
data: {}
} )
.done( function( citoidData ) {
dfd.resolve( citoidData );
} )
.fail( function( data ) {
mw.notify( $.i18n( 'gadget-citetool-error-lookup' ), {
title: $.i18n( 'gadget-citetool-dialog-title' ),
type: 'error',
autohide: true
} );
// Add lookup-date anyway!
var date = new Date();
dfd.resolve( [ { 'accessDate': date.toISOString().slice( 0, 10 ) } ] );
} );
return dfd.promise();
};
wb.datamodel = require( 'wikibase.datamodel' );
var citeTool = new CiteTool();
citeTool.init();
} )( wikibase, window.dataValues || [], mediaWiki, jQuery );