MediaWiki

Difference between revisions of "Common.js"

(in case of Blazegraph error, display it to the user, to avoid they are waiting for nothing ; i18n translation on the page itself with Translate (avoiding the messages are splitted between Translate and editing the JS))
(Removed some hacks, since the source code was fixed and deployed (see https://github.com/lingua-libre/RecordWizard/commits/master))
 
(60 intermediate revisions by 3 users not shown)
Line 1: Line 1:
// Replace Wikidata IDs with their [label, description]
+
// Page-specific scripts modules and conditional loading
if ( $( '.wb-external-id' ).length > 0 ) {
+
// RecordWizard sugar
mw.loader.using( 'mediawiki.ForeignApi', function() {
 
$( '.wb-external-id' ).each( function() {
 
if ( $( this ).attr( 'href' ).lastIndexOf( 'https://www.wikidata.org', 0 ) === 0 ) {
 
var wikidataApi = new mw.ForeignApi( 'https://www.wikidata.org/w/api.php', {
 
anonymous: true,
 
parameters: { 'origin': '*' },
 
ajax: { timeout: 10000 }
 
} ),
 
lang = mw.config.get( 'wgUserLanguage' ),
 
node = $( this );
 
wikidataApi.get( {
 
'action': 'wbgetentities',
 
'format': 'json',
 
'ids': node.text(),
 
'props': 'labels|descriptions',
 
'languages': lang,
 
'languagefallback': 1,
 
'origin': '*'
 
} ).then( function( data ) {
 
                                       
 
var entity = data.entities[ node.text() ],
 
label = ( entity.labels[ lang ] !== undefined ? entity.labels[ lang ].value + ' <i>(' + node.text() + ')</i>' : node.text() ),
 
description = ( entity.descriptions[ lang ] !== undefined ? '<small>' + entity.descriptions[ lang ].value + '</small>' : '' );
 
 
node.html( label + '<br>' + description )
 
} );
 
}
 
} );
 
} );
 
}
 
 
 
//Add an audio player to the audio records links in the wikibase items
 
const BASE_FILE_URL = 'https://commons.wikimedia.org/wiki/Special:Redirect/file?wptype=file&wpvalue=';
 
 
 
function playButton( audioUrl ) {
 
var button = new OO.ui.ButtonWidget( {
 
framed: false,
 
icon: 'play',
 
title: 'play'
 
} );
 
button.on( 'click', function() {
 
var audio = new Audio( audioUrl );
 
audio.play();
 
} );
 
 
 
return button.$element;
 
}
 
 
 
if ( $( '#P3 a.extiw' ).length > 0 ) {
 
    mw.loader.using( [ 'oojs-ui-widgets', 'oojs-ui.styles.icons-media' ], function() {
 
        $( '#P3 a.extiw' ).each( function() {
 
            var $node = $( this );
 
            $node.before( playButton( BASE_FILE_URL + $node.text() ) );
 
        } );
 
    } );
 
}
 
 
 
 
 
/**
 
* Display last records on main page
 
**/
 
var ab1, ab2;
 
 
 
 
 
var AudioBox = function( recordQid, $node ) {
 
this.wbRecord = new mw.recordWizard.wikibase.Item( recordQid );
 
 
this.$node = $node;
 
this.audioNode = document.createElement( 'audio' );
 
this.audioNode.preload = 'auto';
 
 
 
    this.api = new mw.Api();
 
 
 
this.recordQid = recordQid;
 
this.langQid = null;
 
this.speakerQid = null;
 
 
this.label = '';
 
this.media = '';
 
this.lang = '';
 
this.speaker = '';
 
 
this.wbRecord.getFromApi( this.api ).then( this.processRecord.bind( this ), displayError );
 
}
 
 
 
AudioBox.prototype.processRecord = function() {
 
this.label = this.wbRecord.getLabel( 'en' );
 
this.media = 'https://commons.wikimedia.org/wiki/Special:FilePath/' + this.wbRecord.getStatements( 'P3' )[ 0 ].getValue();
 
this.langQid = this.wbRecord.getStatements( 'P4' )[ 0 ].getValue();
 
this.speakerQid = this.wbRecord.getStatements( 'P5' )[ 0 ].getValue();
 
  
this.api.get( {
+
/* *************************************************************** */
action: "wbgetentities",
+
/* PAGE-SPECIFIC SCRIPTS LOADINGS ******************************** */
format: "json",
+
// Documentation: https://m.mediawiki.org/wiki/ResourceLoader/Architecture#Resource:_Scripts
ids: this.langQid + '|' + this.speakerQid,
+
// Documentation: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.loader
props: "labels",
+
//🎷🥁🎸🐣📔🗻🌏
languages: mw.config.get( 'wgUserLanguage' ) + "|en",
+
// LastAudiosBoxes
languagefallback: 1,
+
if (/^LinguaLibre:Main/.test(mw.config.get('wgPageName'))) {
} ).then( this.processLabels.bind( this ), displayError );
+
  console.log('🎸 Last Audios');
 +
  mw.loader.load('/index.php?title=MediaWiki:LastAudios.js&action=raw&ctype=text/javascript');
 
}
 
}
 
+
// SoundLibrary
AudioBox.prototype.processLabels = function( data ) {
+
if (/^LinguaLibre:Explore_the_sound_library(\/[a-z_-]+)?$/.test(mw.config.get('wgPageName'))) {
var langLabels;
+
  console.log('🥁 Sound library');
 
+
  mw.loader.load('/index.php?title=MediaWiki:SoundLibrary.js&action=raw&ctype=text/javascript');
if ( data.entities === undefined || data.entities[ this.langQid ] === undefined || data.entities[ this.speakerQid ] === undefined ) {
 
displayError( 'dataerror' );
 
return;
 
}
 
langLabels = data.entities[ this.langQid ].labels;
 
 
if ( langLabels[ mw.config.get( 'wgUserLanguage' ) ] !== undefined ) {
 
this.lang = langLabels[ mw.config.get( 'wgUserLanguage' ) ].value;
 
} else {
 
this.lang = langLabels[ 'en' ].value;
 
}
 
 
this.speaker = data.entities[ this.speakerQid ].labels[ 'en' ].value;
 
 
 
this.display();
 
 
}
 
}
 
+
// Item pages additional sugar
AudioBox.prototype.display = function() {
+
if (/^(Q|Property:P)[0-9]+$/.test(mw.config.get('wgPageName'))) {
this.$node.find( '.ab-title' ).text( this.label );
+
  console.log('🍭 ItemsSugar ');
this.$node.find( '.ab-metadata' ).text( this.lang + ' - ' + this.speaker );
+
  mw.loader.load('/index.php?title=MediaWiki:ItemsSugar.js&action=raw&ctype=text/javascript');
 
this.audioNode.src = this.media;
 
this.$node.find( '.ab-playbutton' ).click( this.audioNode.play.bind( this.audioNode ) );
 
 
}
 
}
 
+
// Lexeme Queries Generator (OOUI)
 
+
if (/^Template:LexemeQueriesGenerator$/.test(mw.config.get('wgPageName')) || /^Help:SPARQL_/.test(mw.config.get('wgPageName'))) {
 
+
  console.log('📔 Lexeme Queries Generator');
 
+
  mw.loader.load('/index.php?title=MediaWiki:LexemeQueriesGenerator.js&action=raw&ctype=text/javascript');
 
 
 
 
function createAudioBoxes( data ) {
 
if ( data.query === undefined || data.query.rwrecords === undefined || data.query.rwrecords.length < 2 ) {
 
displayError( 'nodata' );
 
return;
 
}
 
 
 
ab1 = new AudioBox( data.query.rwrecords[ 0 ], $( '.audiobox' ).eq( 0 ) );
 
ab2 = new AudioBox( data.query.rwrecords[ 1 ], $( '.audiobox' ).eq( 1 ) );
 
 
}
 
}
 
+
// SPARQL to data (OOUI)
function getLastRecords() {
+
if (/SPARQL/.test(mw.config.get('wgPageName')) || /parql/.test(mw.config.get('wgPageName'))) {
    var api = new mw.Api();
+
  console.log('🚀 SPARQL to data');
api.get( {
+
  mw.loader.load('/index.php?title=MediaWiki:Sparql2data.js&action=raw&ctype=text/javascript');
        action: 'query',
 
        format: 'json',
 
        list: 'rwrecords',
 
        rwrlimit: '2',
 
rwrsort: 'pageid',
 
rwrdir: 'descending',
 
rwrformat: 'qid'
 
  } ).then( createAudioBoxes, displayError );
 
 
}
 
}
 
+
// Click bug & browser
function displayError( code, error ) {
+
if (/browser/.test(mw.config.get('wgPageName')) || /bug/.test(mw.config.get('wgPageName'))) {
console.warn( code, error );
+
  console.log('🎷 Click bug & browser');
 +
  mw.loader.load('/index.php?title=MediaWiki:Browser.js&action=raw&ctype=text/javascript');
 
}
 
}
 
+
/* *************************************************************** */
if ( mw.config.get( 'wgPageName' ) === 'LinguaLibre:Main_Page' ) {
+
/* In development *************************************** */
    mw.loader.using( [ 'mediawiki.api', 'ext.recordWizard.wikibase' ] ).then( getLastRecords );
+
// User:Yug: Language overview
 +
if (/Languages/.test(mw.config.get('wgPageName'))) {
 +
  console.log('🌏 Languages gallery');
 +
  mw.loader.load('/index.php?title=MediaWiki:LanguagesGallery.js&action=raw&ctype=text/javascript');
 
}
 
}
 
+
// User:Yug: Search by words, see https://jsfiddle.net/hugolpz/ecpzy0fo/89/
// T206801 - Links to Wikipedia contain "+" (instead of "_")
+
if (/^LinguaLibre:Search_by(\/[a-z_-]+)?$/.test(mw.config.get('wgPageName'))) {
if ( $( 'div#P19 .wb-external-id' ).length ) {
+
  console.log('🐣 Search by word');
$( 'div#P19 .wb-external-id' ).attr( 'href', $( 'div#P19 .wb-external-id' ).attr( 'href' ).replace( /\+/g, '_' ) );
+
  mw.loader.load('/index.php?title=MediaWiki:SearchBy.js&action=raw&ctype=text/javascript');
 
}
 
}
 +
// Minorities languages statistics page
  
// Interface for displaying contributions on [[LinguaLibre:Explore the sound library]]
+
// Iframe gallery for Lingualibre:Apps
$( function (){
+
// Prototype https://jsfiddle.net/hugolpz/meygpo6t/
if( !/^LinguaLibre:Explore_the_sound_library(\/[a-z_-]+)?$/.test( mw.config.get( 'wgPageName' ) ) || mw.config.get( 'wgUserName' ) === 'Seb35' || mw.config.get( 'wgUserName' ) === 'Nicolas NALLET' || mw.config.get( 'wgUserName' ) === 'Mathis Back' ) {
+
// Problem: iframe are not accepted into wikicode. Solution: hide key values in html tag, generate iframe via JS injection.
return;
 
}
 
 
 
// Parameters
 
const nbTotalResults = 100;  // Total number of results requested to SPARQL endpoint
 
const nbResultsPerPage = 10; // Number of results displayed per page
 
 
 
// User messages to be translated
 
const messages = {
 
'msg-no-results': 'No results.',
 
'msg-error': 'Error',
 
'msg-other-records': '&nbsp;(and many other results)',
 
'button-gosearch': 'Search',
 
'button-gosearch-last-records': 'Last records',
 
'button-resetsearch': 'Reset',
 
'button-datasets': 'Datasets',
 
'placeholder': 'Select or type',
 
};
 
 
 
function i18n( msg ) {
 
var element = $( '#' + msg );
 
if( element && element.text() !== messages[msg] ) {
 
return element.text();
 
}
 
return messages[msg];
 
}
 
 
 
const userLanguage = mw.config.get( 'wgPageContentLanguage' );
 
mw.loader.using( ['oojs', 'oojs-ui', 'mediawiki.api', 'ext.recordWizard.wikibase'], function () {
 
 
 
oouiSelectors = {
 
'speaker': null,
 
'gender': null,
 
'language': null,
 
'proficiency': null,
 
};
 
var sparqlGlobal = {
 
'speaker': 'SELECT ?speaker ?speakerLabel ?gender ?language ?proficiency WHERE { ?speaker prop:P2 entity:Q3 . OPTIONAL { ?speaker prop:P8 ?gender } . OPTIONAL { ?speaker llp:P4 ?statement . ?statement llv:P4 ?language . OPTIONAL { ?statement llq:P16 ?proficiency } } . SERVICE wikibase:label { bd:serviceParam wikibase:language "' + userLanguage + ',fr,en" } } ORDER BY ?speakerLabel',
 
'gender': 'SELECT DISTINCT ?gender ?genderLabel WHERE { ?gender prop:P2 entity:Q7 . SERVICE wikibase:label { bd:serviceParam wikibase:language "' + userLanguage + ',fr,en" } } ORDER BY ?gender',
 
'language': 'SELECT DISTINCT ?language ?languageLabel WHERE { ?language prop:P2 entity:Q4 . SERVICE wikibase:label { bd:serviceParam wikibase:language "' + userLanguage + ',fr,en" } } ORDER BY ?languageLabel',
 
'proficiency': 'SELECT DISTINCT ?proficiency ?proficiencyLabel WHERE { ?proficiency prop:P2 entity:Q5 . SERVICE wikibase:label { bd:serviceParam wikibase:language "' + userLanguage + ',fr,en" } } ORDER BY ?proficiency',
 
};
 
const htmlElements = {
 
'speaker': '#sndlib-filteruser',
 
'gender': '#sndlib-filtergender',
 
'language': '#sndlib-filterlanguage',
 
'proficiency': '#sndlib-filterlevelofproficiency',
 
};
 
const unknown = {};
 
 
 
request = {
 
'speaker': '',
 
'gender': '',
 
'language': '',
 
'proficiency': '',
 
};
 
speakersCriteria = [];
 
list = {
 
'speaker': null,
 
'gender': null,
 
'language': null,
 
'proficiency': null,
 
};
 
mapping = {
 
'speaker': null,
 
'gender': null,
 
'language': null,
 
'proficiency': null,
 
};
 
automaticField = {
 
'speaker': null,
 
'gender': null,
 
'language': null,
 
'proficiency': null,
 
};
 
 
 
// Helper function to obtain the Qvalue of some selector
 
function getQValue( type ) {
 
var rawValue;
 
if( type === 'speaker' || type === 'language' ) {
 
rawValue = oouiSelectors[type].getValue();
 
} else {
 
rawValue = oouiSelectors[type].getMenu().findSelectedItem() ? oouiSelectors[type].getMenu().findSelectedItem().getData() : '';
 
}
 
rawValue = rawValue.trim();
 
if( !rawValue ) {
 
return null;
 
} else if( /^Q[0-9]+$/.test( rawValue ) ) {
 
return rawValue;
 
} else if( /\((Q[0-9]+)\)$/.test( rawValue ) ) {
 
return rawValue.replace( /.*\((Q[0-9]+)\)$/, '$1' );
 
} else {
 
return '';
 
}
 
}
 
 
 
// Helper function to dedupe a sorted list
 
function dedupe( arr, fn ) {
 
if( fn ) {
 
return arr.filter( function( x, i, a ) {
 
return !i || fn( x ) !== fn( a[i-1] );
 
} );
 
}
 
return arr.filter( function( x, i, a ) {
 
return !i || x !== a[i-1];
 
} );
 
}
 
 
 
// Returns the list of items of a given type for a restricted list of Qids
 
function getListSelectorFromQids( type, qids ) {
 
const indexes = dedupe( qids.map( function( x ) {
 
return mapping[type][x];
 
} ).sort( function( a, b ) { return a-b; } ) );
 
const r = indexes.map( function( x ) {
 
return list[type][x];
 
} );
 
return r;
 
}
 
 
 
// Initialise a selector and keep the list in memory the result for later reuse
 
function updateSelector( type, force ) {
 
return function( values, emptyValue ) {
 
 
 
if( emptyValue === undefined ) {
 
emptyValue = true;
 
}
 
 
 
console.debug( 'updateSelector('+type+','+force+')('+(values?values.length:'null')+' values,'+(emptyValue===true?'true':(emptyValue===null?'null':(emptyValue===false?'false':'autre')))+')' );
 
 
 
const widgetType = type === 'speaker' || type === 'language' ? 'ComboBoxInputWidget' : 'DropdownWidget';
 
 
 
if( !list[type] ) {
 
 
 
// Save the list in a global variable to quickly filter then
 
// We dedupe because the 'speaker' (only) has duplicate (ordered) values
 
const v = values.results.bindings;
 
list[type] = dedupe( v, function( x ) { return x[type].value; } );
 
if( widgetType === 'DropdownWidget' ) {
 
var emptyItem = [ {} ];
 
emptyItem[0][type] = { value: '' };
 
emptyItem[0][type+'Label'] = { value: ' ' };
 
list[type] = emptyItem.concat( list[type] );
 
}
 
values = list[type];
 
 
 
// Save the mapping Qxx → index to update the list from a list of Qxx (see doQuery)
 
console.debug( type, list[type] );
 
mapping[type] = list[type].reduce( function( o, x, i ) {
 
o[ x[type].value.substr( 31 ) ] = i;
 
return o;
 
}, {} );
 
 
 
// Save the global matrix: speaker x gender x language x proficiency
 
if( type === 'speaker' ) {
 
speakersCriteria = v.map( function( x ) {
 
return [
 
x.speaker.value.substr( 31 ),
 
!x.gender || x.gender.type === 'bnode' ? unknown : x.gender.value.substr( 31 ),
 
!x.language || x.language.type === 'bnode' ? unknown : x.language.value.substr( 31 ),
 
!x.proficiency || x.proficiency.type === 'bnode' ? unknown : x.proficiency.value.substr( 31 ),
 
];
 
} );
 
}
 
}
 
 
 
// Never update manual selectors (except if the user forces the reset)
 
if( !force && getQValue( type ) && automaticField[type] === false ) {
 
console.debug( 'updateSelector('+type+','+force+')('+(values?values.length:'null')+' values): abort1' );
 
return;
 
}
 
 
 
if( force || values === null ) {
 
values = list[type];
 
}
 
 
 
// If there are at least one unknown value, reset the selector
 
else if( values.filter( function( x ) { return x === undefined; } ).length ) {
 
values = list[type];
 
console.debug( 'updateSelector('+type+','+force+')('+(values?values.length:'null')+' values): abort2' );
 
}
 
 
 
// For dropdowns, add an empty item if there is none
 
if( widgetType === 'DropdownWidget' && values.length && values[0][type].value ) {
 
var emptyItem = [ {} ];
 
emptyItem[0][type] = { value: '' };
 
emptyItem[0][type+'Label'] = { value: ' ' };
 
values = emptyItem.concat( values );
 
}
 
 
 
// Create the OOUI selector
 
oouiSelectors[type] = new OO.ui[widgetType]( {
 
placeholder: i18n( 'placeholder' ),
 
menu: {
 
filterFromInput: true,
 
items: values.map( function( x ) {
 
return new OO.ui.MenuOptionWidget( {
 
data: widgetType === 'ComboBoxInputWidget' ? x[ type + 'Label' ].value + " (" + x[type].value.substr( 31 ) + ")" : x[type].value.substr( 31 ),
 
label: x[ type + 'Label' ].value
 
} );
 
} )
 
}
 
} );
 
if( widgetType === 'DropdownWidget' && values.length > 2 ) {
 
oouiSelectors[type].getMenu().items[0].toggle( false );
 
}
 
 
 
if( widgetType === 'ComboBoxInputWidget' && values.length === 1 && automaticField[type] !== false ) {
 
oouiSelectors[type].setValue( values[0][ type + 'Label' ].value + " (" + values[0][type].value.substr( 31 ) + ")" );
 
// For dropdown, when there is one real value, there is also the empty value as first option
 
} else if( widgetType === 'DropdownWidget' && values.length === 2 && automaticField[type] !== false ) {
 
oouiSelectors[type].getMenu().selectItemByData( values[1][type].value.substr( 31 ) );
 
}
 
 
 
$( htmlElements[type] ).html('').append(
 
oouiSelectors[type].$element
 
);
 
if( !force ) {
 
console.debug( '!force', type, getQValue( type ), automaticField[type] );
 
if( automaticField[type] !== false ) {
 
automaticField[type] = getQValue( type ) ? true : emptyValue;
 
}
 
console.debug( 'updateSelector('+type+','+force+')('+(values?values.length:'null')+' values): on passe en automatique' );
 
} else {
 
automaticField[type] = null;
 
console.debug( 'updateSelector('+type+','+force+')('+(values?values.length:'null')+' values): on passe en manuel' );
 
}
 
 
 
if( widgetType === 'ComboBoxInputWidget' ) {
 
oouiSelectors[type].on( 'change', function() {
 
if( getQValue( type ) === '' ) {
 
return;
 
}
 
onChange( type );
 
} );
 
} else {
 
oouiSelectors[type].getMenu().on( 'select', function( item ) {
 
if( item.getData() === '' ) {
 
item.toggle( false );
 
} else {
 
oouiSelectors[type].getMenu().items[0].toggle( true );
 
}
 
onChange( type );
 
} );
 
}
 
 
 
};
 
}
 
 
 
function onChange( type ) {
 
 
 
// Logique ternaire pour les sélecteurs :
 
// - null := sélecteur indéterminé, pouvant être réduit sous l’influence d’autres sélecteurs
 
// - false := sélecteur manuel, n’étant jamais réduit sous l’influence d’autres sélecteurs et étant toujours étendus (=comprend toutes les valeurs possibles)
 
// - true := sélecteur automatique, étant toujours réduit sous l’influence d’autres sélecteurs
 
 
 
console.debug( 'onChange('+type+')' );
 
var state = {
 
speaker: getQValue( 'speaker' ),
 
gender: getQValue( 'gender' ),
 
language: getQValue( 'language' ),
 
proficiency: getQValue( 'proficiency' ),
 
};
 
var nbFilled = 1, emptyValue = true;
 
if( state[type] ) {
 
if( state[type] != request[type] ) {
 
automaticField[type] = false;
 
console.debug( 'onChange('+type+'): on passe en manuel' );
 
}
 
} else {
 
automaticField[type] = null;
 
emptyValue = null;
 
nbFilled = ( automaticField.language === false && state.language ? 1 : 0 )
 
+ ( automaticField.speaker === false && state.speaker ? 1 : 0 )
 
+ ( automaticField.gender === false && state.gender ? 1 : 0 )
 
+ ( automaticField.proficiency === false && state.proficiency ? 1 : 0 );
 
if( nbFilled >= 1 ) {
 
automaticField[type] = false;
 
emptyValue = true;
 
if( type !== 'speaker' && !state.speaker && automaticField.speaker !== false ) {
 
automaticField.speaker = emptyValue;
 
}
 
if( type !== 'gender' && !state.gender && automaticField.gender !== false ) {
 
automaticField.gender = emptyValue;
 
}
 
if( type !== 'language' && !state.language && automaticField.language !== false ) {
 
automaticField.language = emptyValue;
 
}
 
if( type !== 'proficiency' && !state.proficiency && automaticField.proficiency !== false ) {
 
automaticField.proficiency = emptyValue;
 
}
 
} else {
 
emptyValue = null;
 
if( !state.speaker ) {
 
automaticField.speaker = emptyValue;
 
}
 
if( !state.gender ) {
 
automaticField.gender = emptyValue;
 
}
 
if( !state.language ) {
 
automaticField.language = emptyValue;
 
}
 
if( !state.proficiency ) {
 
automaticField.proficiency = emptyValue;
 
}
 
}
 
}
 
console.debug( nbFilled, state[type], request[type], emptyValue, automaticField );
 
 
 
// Do not reduce fields above one change by the user, else it leads to a bad User Experience
 
// because fields change themselves with a hardly-understandable logic
 
//var nbFilled = ( state.language ? 1 : 0 ) + ( state.speaker ? 1 : 0 ) + ( state.gender ? 1 : 0 ) + ( state.proficiency ? 1 : 0 );
 
//if( nbFilled > 1 ) {
 
// return;
 
//}
 
 
 
var potentialSpeakers = speakersCriteria;
 
 
 
if( state.language && automaticField.language === false ) {
 
potentialSpeakers = reduceSpeakers( 2, state.language, potentialSpeakers );
 
}
 
if( state.speaker && automaticField.speaker === false ) {
 
potentialSpeakers = reduceSpeakers( 0, state.speaker, potentialSpeakers );
 
}
 
if( state.gender && automaticField.gender === false ) {
 
potentialSpeakers = reduceSpeakers( 1, state.gender, potentialSpeakers );
 
}
 
if( state.proficiency && automaticField.proficiency === false ) {
 
potentialSpeakers = reduceSpeakers( 3, state.proficiency, potentialSpeakers );
 
}
 
 
 
var listSpeaker = getListSelectorFromQids( 'speaker', dedupe( potentialSpeakers.map( function( x ) { return x[0]; } ) ) );
 
var listGender = getListSelectorFromQids( 'gender', dedupe( potentialSpeakers.map( function( x ) { return x[1]; } ) ) );
 
var listLanguage = getListSelectorFromQids( 'language', dedupe( potentialSpeakers.map( function( x ) { return x[2]; } ) ) );
 
var listProficiency = getListSelectorFromQids( 'proficiency', dedupe( potentialSpeakers.map( function( x ) { return x[3]; } ) ) );
 
 
 
// Update the selectors with the restricted values
 
updateSelector( 'speaker' )( listSpeaker, emptyValue );
 
updateSelector( 'gender' )( listGender, emptyValue );
 
updateSelector( 'language' )( listLanguage, emptyValue );
 
updateSelector( 'proficiency' )( listProficiency, emptyValue );
 
 
 
request = {
 
speaker: getQValue( 'speaker' ),
 
gender: getQValue( 'gender' ),
 
language: getQValue( 'language' ),
 
proficiency: getQValue( 'proficiency' ),
 
};
 
console.debug( automaticField );
 
}
 
 
 
function initSelectors() {
 
 
 
$.getJSON(
 
'https://lingualibre.org/bigdata/namespace/wdq/sparql',
 
{
 
query: sparqlGlobal['speaker']
 
}
 
).then( updateSelector( 'speaker', true ), displayErrorToUser );
 
 
 
$.getJSON(
 
'https://lingualibre.org/bigdata/namespace/wdq/sparql',
 
{
 
query: sparqlGlobal['gender']
 
}
 
).then( updateSelector( 'gender', true ), displayErrorToUser );
 
 
 
$.getJSON(
 
'https://lingualibre.org/bigdata/namespace/wdq/sparql',
 
{
 
query: sparqlGlobal['language']
 
}
 
).then( updateSelector( 'language', true ), displayErrorToUser );
 
 
 
$.getJSON(
 
'https://lingualibre.org/bigdata/namespace/wdq/sparql',
 
{
 
query: sparqlGlobal['proficiency']
 
}
 
).then( updateSelector( 'proficiency', true ), displayErrorToUser );
 
 
 
$('#sndlib-savefiltersearch').wrapInner( '<a style="color:white"></a>' );
 
}
 
 
 
function reset() {
 
request = {
 
speaker: '',
 
gender: '',
 
language: '',
 
proficiency: '',
 
};
 
automaticField = {
 
'speaker': null,
 
'gender': null,
 
'language': null,
 
'proficiency': null,
 
};
 
updateSelector( 'speaker', true )( list.speaker );
 
updateSelector( 'gender', true )( list.gender );
 
updateSelector( 'language', true )( list.language );
 
updateSelector( 'proficiency', true )( list.proficiency );
 
$( '#sndlib-audioresults' ).html( '' );
 
$( '#sndlib-audioPages' ).html( '' );
 
}
 
 
 
function reduceSpeakers( index, value, arr ) {
 
return arr.filter( function( x ) {
 
return x[index] === value;
 
} );
 
}
 
 
 
// Add button 'Search'
 
$( '#sndlib-gosearch' ).html('').append(
 
( new OO.ui.ButtonInputWidget( {
 
label: i18n( 'button-gosearch' )
 
} ) ).on( 'click', factoryDoQuery( 'random' ) ).$element
 
);
 
 
 
$( '#sndlib-gosearch-last-records' ).html('').append(
 
( new OO.ui.ButtonInputWidget( {
 
label: i18n( 'button-gosearch-last-records' )
 
} ) ).on( 'click', factoryDoQuery( 'last' ) ).$element
 
);
 
 
 
// Add button 'Reset'
 
$( '#sndlib-resetsearch' ).html('').append(
 
( new OO.ui.ButtonInputWidget( {
 
label: i18n( 'button-resetsearch' )
 
} ) ).on( 'click', reset ).$element
 
);
 
 
 
// Add button 'Datasets' - it seems that OOUI ButtonWidget.setHref does not work
 
var buttonDatasetsA = $( '<a href="https://lingualibre.org/datasets/"></a>' );
 
var buttonDatasets = new OO.ui.ButtonInputWidget( {
 
label: i18n( 'button-datasets' )
 
} );
 
var buttonDatasetsLinked = buttonDatasetsA.append( buttonDatasets.$element );
 
$( '#sndlib-datasetsButton' ).html('').append( buttonDatasetsLinked );
 
 
 
function setDownloadLink( data ) {
 
if ( data.results === undefined || data.results.bindings === undefined ) {
 
displayErrorToUser( 'error: no result from SPARQL' );
 
return;
 
}
 
 
 
function escapeCsv( val ) {
 
if( !val ) {
 
return '""';
 
}
 
return '"' + val.value.replaceAll( '"', '""' ) + '"';
 
}
 
 
var csv = 'data:application/csv;charset=utf-8,' + encodeURIComponent( data.results.bindings.map( function( x ) {
 
return [
 
escapeCsv( x.speakerName ),
 
escapeCsv( x.genderLabel ),
 
escapeCsv( x.languageLabel ),
 
escapeCsv( x.proficiencyLabel ),
 
escapeCsv( x.word ),
 
escapeCsv( x.file ),
 
escapeCsv( x.date ),
 
].join( ',' );
 
} ).join( "\n" ) );
 
 
 
$('#sndlib-savefiltersearch a').attr( {
 
download: 'export-lili.csv',
 
href: csv,
 
} );
 
}
 
 
 
function displayErrorToUser( code, error ) {
 
displayError( code, error );
 
$( '#sndlib-audioresults' ).html( i18n( 'msg-error' ) + ' (' + code + ')' + ( error ? ' ' + error : '' ) );
 
}
 
 
 
// Display results
 
function createAudioBoxesForSearch( data ) {
 
if ( data.results === undefined || data.results.bindings === undefined ) {
 
displayErrorToUser( 'error: no result from SPARQL' );
 
return;
 
}
 
if ( data.results.bindings.length < 1 ) {
 
$( '#sndlib-audioresults' ).html( i18n( 'msg-no-results' ) );
 
$( '#sndlib-audioPages' ).html( '' );
 
return;
 
}
 
var length = data.results.bindings.length <= nbTotalResults ? data.results.bindings.length : nbTotalResults;
 
var moreResults = data.results.bindings.length > nbTotalResults;
 
console.debug( 'SPARQL results', data.results.bindings );
 
function displayAudioBoxes( index ) {
 
$( '#sndlib-audioresults' ).html( '' );
 
for( var i = 0; i < nbResultsPerPage && i+index*nbResultsPerPage < length; i++ ) {
 
var box = $( '<div class="audiobox"> <div class="ab-playbutton"><i></i></div> <div> <div class="ab-title">...</div> <div class="ab-metadata">...</div> </div> </div>' );
 
var audiobox = new AudioBox( data.results.bindings[ i+index*nbResultsPerPage ].record.value.substr( 31 ), box );
 
$( '#sndlib-audioresults' ).append( box );
 
}
 
function applyFn( i ) {
 
return function() {
 
displayAudioBoxes( i );
 
return false;
 
}
 
}
 
$( '#sndlib-audioPages' ).html( '' );
 
if( length > nbResultsPerPage ) {
 
for( var i = 0; i < Math.ceil( length/nbResultsPerPage ); i++ ) {
 
var link = $( '<a href="#"' + ( i === index ? ' class="selected"' : '' ) + '>' + (i+1) + '</a>' ).on( 'click', applyFn( i ) );
 
$( '#sndlib-audioPages' ).append( link ).append( i < Math.ceil( length/nbResultsPerPage )-1 ? '&nbsp;' : '' );
 
}
 
}
 
if( moreResults ) {
 
$( '#sndlib-audioPages' ).append( i18n( 'msg-other-records' ) );
 
}
 
}
 
displayAudioBoxes( 0 );
 
 
 
var recordsEntities = data.results.bindings.map( function( x ) {
 
return 'entity:' + x.record.value.substr( 31 );
 
} ).join( ',' );
 
console.debug( recordsEntities );
 
console.debug( 'SELECT ?record ?speakerName ?languageLabel ?word ?file ?date ?proficiencyLabel ?genderLabel WHERE { ?record prop:P2 entity:Q2 ; prop:P4 ?language ; prop:P5 ?speaker ; prop:P7 ?word ; prop:P3 ?file ; prop:P6 ?date . ?speaker llp:P4 [ llv:P4 ?language ; llq:P16 ?proficiency ] ; prop:P8 ?gender . ?speaker rdfs:label ?speakerName . FILTER( ?record IN (' + recordsEntities + ') ) . FILTER( LANG( ?speakerName ) = "en" ) . SERVICE wikibase:label { bd:serviceParam wikibase:language "' + userLanguage + ',fr,en" } }' );
 
 
 
$.getJSON(
 
'https://lingualibre.org/bigdata/namespace/wdq/sparql',
 
{
 
query: 'SELECT ?record ?speakerName ?languageLabel ?word ?file ?date ?proficiencyLabel ?genderLabel WHERE { ?record prop:P2 entity:Q2 ; prop:P4 ?language ; prop:P5 ?speaker ; prop:P7 ?word ; prop:P3 ?file ; prop:P6 ?date . ?speaker llp:P4 [ llv:P4 ?language ; llq:P16 ?proficiency ] ; prop:P8 ?gender . ?speaker rdfs:label ?speakerName . FILTER( ?record IN (' + recordsEntities + ') ) . FILTER( LANG( ?speakerName ) = "en" ) . SERVICE wikibase:label { bd:serviceParam wikibase:language "' + userLanguage + ',fr,en" } }'
 
}
 
).then( setDownloadLink, displayErrorToUser );
 
 
 
}
 
 
 
// Do SPARQL request from filters
 
function factoryDoQuery( order ) {
 
 
 
return function doQuery() {
 
 
 
request = {
 
speaker: getQValue( 'speaker' ),
 
gender: getQValue( 'gender' ),
 
language: getQValue( 'language' ),
 
proficiency: getQValue( 'proficiency' ),
 
order: order,
 
};
 
 
 
var potentialSpeakers = speakersCriteria;
 
 
 
var query = "SELECT ?record ?date WHERE { ?record prop:P2 entity:Q2 ; prop:P4 ?language ; prop:P5 ?speaker ; prop:P6 ?date . ";
 
if( request.language ) {
 
query += "?record prop:P4 entity:" + request.language + " . ";
 
potentialSpeakers = reduceSpeakers( 2, request.language, potentialSpeakers );
 
}
 
if( request.speaker ) {
 
query += "?record prop:P5 entity:" + request.speaker + " . ";
 
potentialSpeakers = reduceSpeakers( 0, request.speaker, potentialSpeakers );
 
}
 
if( request.gender ) {
 
potentialSpeakers = reduceSpeakers( 1, request.gender, potentialSpeakers );
 
}
 
if( request.proficiency ) {
 
potentialSpeakers = reduceSpeakers( 3, request.proficiency, potentialSpeakers );
 
query += "?speaker llp:P4 [ llv:P4 ?language ; llq:P16 entity:" + request.proficiency + " ] . ";
 
}
 
if( potentialSpeakers.length < speakersCriteria.length ) {
 
var listSpeakers = dedupe( potentialSpeakers.map( function( x ) {
 
return 'entity:' + x[0];
 
} ) ).join();
 
console.debug( speakersCriteria, potentialSpeakers, listSpeakers );
 
if( listSpeakers.length < 70 ) {
 
query += 'FILTER( ?speaker IN (' + listSpeakers + ') )';
 
}
 
}
 
query += '}';
 
if( order === 'random' ) {
 
query += ' ORDER BY RAND()';
 
} else if( order === 'last' ) {
 
query += ' ORDER BY DESC(?date)';
 
}
 
query += ' LIMIT ' + (nbTotalResults+1);
 
if( order === 'random' ) {
 
query += '\n# ' + Date.now();
 
}
 
 
 
// Voir https://commons.wikimedia.org/wiki/Category:Throbbers - il faut que le fond soit transparent
 
$( '#sndlib-audioresults' ).html( '' ).append( '<img src="https://upload.wikimedia.org/wikipedia/commons/7/7a/Balls.gif" width="50" />' );
 
$( '#sndlib-audioPages' ).html( '' );
 
 
 
// Execute the request
 
var result = $.getJSON(
 
'https://lingualibre.org/bigdata/namespace/wdq/sparql',
 
{
 
query: query,
 
//Accept: 'application/sparql-results+json'
 
}
 
);
 
 
 
result.then( createAudioBoxesForSearch, displayErrorToUser );
 
}
 
}
 
 
 
initSelectors();
 
 
 
} );
 
  
} );
 
  
/*Confirmation message when users try to leave the Record Wizard
+
/*Query visualisation: Replace loading text by a spinner gif***********************************************************/
(works from the second step onwards) */
+
$( '.queryviz-loading' ).html( '<center><img src="https://upload.wikimedia.org/wikipedia/commons/7/7a/Balls.gif" width="50" /></center>' );
$( function() {
 
if(mw.config.get( 'wgPageName' ) !== 'Special:RecordWizard') {
 
return;
 
}
 
window.onbeforeunload = function() {
 
  return "Do you want to leave the page? All your unsaved changes will be lost.";
 
};
 
});
 

Latest revision as of 08:51, 20 April 2023

// Page-specific scripts modules and conditional loading
// RecordWizard sugar

/* *************************************************************** */
/* PAGE-SPECIFIC SCRIPTS LOADINGS ******************************** */
// Documentation: https://m.mediawiki.org/wiki/ResourceLoader/Architecture#Resource:_Scripts
// Documentation: https://doc.wikimedia.org/mediawiki-core/master/js/#!/api/mw.loader
//🎷🥁🎸🐣📔🗻🌏
// LastAudiosBoxes
if (/^LinguaLibre:Main/.test(mw.config.get('wgPageName'))) {
  console.log('🎸 Last Audios');
  mw.loader.load('/index.php?title=MediaWiki:LastAudios.js&action=raw&ctype=text/javascript');
}
// SoundLibrary
if (/^LinguaLibre:Explore_the_sound_library(\/[a-z_-]+)?$/.test(mw.config.get('wgPageName'))) {
  console.log('🥁 Sound library');
  mw.loader.load('/index.php?title=MediaWiki:SoundLibrary.js&action=raw&ctype=text/javascript');
}
// Item pages additional sugar
if (/^(Q|Property:P)[0-9]+$/.test(mw.config.get('wgPageName'))) {
  console.log('🍭 ItemsSugar ');
  mw.loader.load('/index.php?title=MediaWiki:ItemsSugar.js&action=raw&ctype=text/javascript');
}
// Lexeme Queries Generator (OOUI)
if (/^Template:LexemeQueriesGenerator$/.test(mw.config.get('wgPageName')) || /^Help:SPARQL_/.test(mw.config.get('wgPageName'))) {
  console.log('📔 Lexeme Queries Generator');
  mw.loader.load('/index.php?title=MediaWiki:LexemeQueriesGenerator.js&action=raw&ctype=text/javascript');
}
// SPARQL to data (OOUI)
if (/SPARQL/.test(mw.config.get('wgPageName')) || /parql/.test(mw.config.get('wgPageName'))) {
  console.log('🚀 SPARQL to data');
  mw.loader.load('/index.php?title=MediaWiki:Sparql2data.js&action=raw&ctype=text/javascript');
}
// Click bug & browser
if (/browser/.test(mw.config.get('wgPageName')) || /bug/.test(mw.config.get('wgPageName'))) {
  console.log('🎷 Click bug & browser');
  mw.loader.load('/index.php?title=MediaWiki:Browser.js&action=raw&ctype=text/javascript');
}
/* *************************************************************** */
/* In development *************************************** */
// User:Yug: Language overview
if (/Languages/.test(mw.config.get('wgPageName'))) {
  console.log('🌏 Languages gallery');
  mw.loader.load('/index.php?title=MediaWiki:LanguagesGallery.js&action=raw&ctype=text/javascript');
}
// User:Yug: Search by words, see https://jsfiddle.net/hugolpz/ecpzy0fo/89/
if (/^LinguaLibre:Search_by(\/[a-z_-]+)?$/.test(mw.config.get('wgPageName'))) {
  console.log('🐣 Search by word');
  mw.loader.load('/index.php?title=MediaWiki:SearchBy.js&action=raw&ctype=text/javascript');
}
// Minorities languages statistics page

// Iframe gallery for Lingualibre:Apps
// Prototype https://jsfiddle.net/hugolpz/meygpo6t/
// Problem: iframe are not accepted into wikicode. Solution: hide key values in html tag, generate iframe via JS injection.


/*Query visualisation: Replace loading text by a spinner gif***********************************************************/
$( '.queryviz-loading' ).html( '<center><img src="https://upload.wikimedia.org/wikipedia/commons/7/7a/Balls.gif" width="50" /></center>' );