User:TomT0m/LexToWiktionary.js

From Wikidata
Jump to navigation Jump to search

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
/*jshint esversion: 8 */



mw.loader.using(["oojs-ui.styles.icons-editing-advanced"],
function() {
	"use strict";
	
	// only activate on lexeme namespace
	let namespace = mw.config.get("wgNamespaceNumber");
	if (namespace != mw.config.get("wgNamespaceIds").lexeme){
		return;
	}
	
	$( `<style>
dl.wiktionaries_links dd {
  display: inline;
  margin: 0.3em;
  padding : .3em ;
}
dl.wiktionaries_links dd:hover {
  /*
  same as rule : on vector style
    .uls-language-block > ul > li:hover 
  */ 
  background-color: #eaeff7;
}

dl.wiktionaries_links dt:before{
  display: block;
  content: '';
}

dl.wiktionaries_links dd.iw_1
{
  margin-left: 2em;
}

dl.wiktionaries_links dt{
  /*display: inline-block;*/
  min-width: 100px;
  padding-left : .5em ;
}
.wikt-iw-container{
	background-color: #fcfcfc;
}

dl.wiktionaries_links dd a {
  cursor: pointer;
  text-decoration: none;
  color: #36c;
  font-size: 1em;
  /*display: inline-block;
  width: 100%;*/
  overflow-x: hidden;
  padding: 8px;
  text-overflow: ellipsis;
  white-space: nowrap;
  vertical-align: middle;
}
</style>`).appendTo( "head" );
	
	
	function add_wiktionaries_without_duplicates(wiktionaries, adds){
		return wiktionaries.concat(
			adds.filter(wikt => ! wiktionaries.includes(wikt))
		);
	}
	
	function select_wiktionaries_with_langs(wiktionaries, langCodes){
		return wiktionaries.filter(
				wikt_id => langCodes.includes(site_details[wikt_id].languageCode)
		);
	}
	
	function choose_preferred_wiktionaries(wiktionaries){
		// listing the wiktionaries 
		// corresponding to the babel boxes of the user
		let userLang = mw.config.get("wgUserLanguage");
		let babel_languages = mw.config.get("wgULSBabelLanguages");
		
		let userlang_wiktionaries = select_wiktionaries_with_langs(
			wiktionaries, [userLang]
		);
		let babel_dicts = select_wiktionaries_with_langs(
			wiktionaries,
			babel_languages
		);
		return add_wiktionaries_without_duplicates(userlang_wiktionaries, babel_dicts);
	}
	
	
	// returns a promise that takes a wiki and a page name and returns
	// a list of wikilinks to the relevant pagenames
	
	function pagename_to_languagelinks(pagenames, wiki_details){
		var api = new mw.ForeignApi( wiki_details.apiUrl );
		// using the "query" api call to get informations on page from titles
		return api.get( {
			action: 'query',
			titles: pagenames,
		} ).then(pages_candidates_answer => { 
			let pages_info = pages_candidates_answer.query.pages;
			// on the answer, the non existing titles recieve a « missing » attribute
			// keeping only those who don’t have
			let pages = Object.keys(pages_info)
						.filter(k => !( "missing" in pages_info[k]))
						.map(k => pages_info[k].title);
			
			let uls_langs = mw.config.get("wgULSLanguages");
			
			// constructing the HTML link from all the informations
			// about the site
			return { 
				wiki	: wiki_details,
				links	: pages.map(
					pagename => 
						$("<a />", {
							href   : wiki_details.pageUrl.replace("$1", pagename),
							name   : wiki_details.shortName,
							append : pagename,
							dir    : $.uls.data.getDir(wiki_details.languageCode),
							title  : uls_langs[wiki_details.languageCode]
						  }
						)
			)};
		});
	}
	
	// wrapping function to map on a list on wiki and returns a list of promises
	// that finds the relevant wikipages on each wiki from the pagenames
	
	// returns a function that will take a detail wiki as parameter and will lauch
	// the treatment with the pagenames as parameters
	function launch_treatment_for_wiki(pagenames, treatment, progress){
		return async (wiki_details) => {
			try {
				let res = await treatment(pagenames, wiki_details);
				progress();
				return res;
			} catch(error){
				progress();
				mw.log("error for wiki" + wiki_details.name, error.toString());
			}
		};
	}
	
	// UI : function that installs the language link button.
	function install_iw_button(content, icon){
		icon = icon ?? "language";  // jshint ignore:line
		let align = "center";
		if (mw.config.get("skin")=="minerva"){
			align = "backwards";
		}
		let iw_popup = 
		 new OO.ui.PopupButtonWidget( { 
			$overlay: true,
			label: 'Wiktionary', 
			indicator: 'down',
			icon : icon,
			infusable:true,
			popup: {
				$content: $("<div>",{class:["gadget-wikt-iw-popup"]}).append(content),
				padded: false,
				align: align,
				width: "50em",
			},
			class:["mw-portlet-lang"],
			id :"gadget-wikt-iw-button"
		} );
		// $("#p-views > .vector-menu-content > .vector-menu-content-list")
		//	.prepend($("<li>").append(iw_popup.$element));
	    // html(iw_popup.$element);
	    
	    $(".gadget-wikt-iw-container").html(iw_popup.$element);
	
		// returns a function that can update the button content
		return (content, icon) => {
			iw_popup.setIcon(icon);
			$(".gadget-wikt-iw-popup").html(content);
		};

	}
	
	///////////////////////////////////////////
	// installation of the gadget localisation depending on the skin
	///////////////////////////////////////////
	
	// installing the gadget view
	function install_gadget_element(){
		let vector2022_place = $("#p-views > .vector-menu-content > .vector-menu-content-list");
	
		if (vector2022_place.length > 0) {
			vector2022_place.prepend($("<li>",{class:"gadget-wikt-iw-container"}));
			return true;
		}
		let minerva_neue = $("#p-tb");
			if (minerva_neue.length == 1){
				$("<li>)").addClass("gadget-wikt-iw-container")
						  .appendTo(minerva_neue);
			return true;
		}
		return false;
	}
	
	// gadget initialisation
	if (! install_gadget_element()){
		console.log("Lexeme wiktionary language links : could not install for this skin ");
		return;
	}
	
	let set_iw_button_content = install_iw_button(new OO.ui.ProgressBarWidget( {
		progress: false
	} ).$element);
	
	let lexeme_id = mw.config.get("wbEntityId");
	// getting the wiktionaries project infos
	let site_details = mw.config.get("wbSiteDetails");
	
	let wiktionaries = Object.keys(site_details)
						.filter(
							projectkey => (site_details[projectkey].group=="wiktionary")
						);
	
	
	// TODO : check if variants of languages works
	
	let default_wiktionaries = choose_preferred_wiktionaries(wiktionaries);
	
	let api = new mw.Api();
	
	// real work :
	//   getting the lemma(s) of the lexeme and their langcodes
	api.get({action:"wbgetentities", ids:lexeme_id})
	.then(async (result) => {
		const entity = result.entities;
		const lemset = entity[lexeme_id].lemmas;
		// computing the lemmas strings
		let lemmas = Object.keys(lemset)
						.map(lang => lemset[lang].value);
						
		// listing the wiktionaries with the same language of the lexeme
		let lemmas_langs = Object.keys(lemset);
		let lexeme_wikts = select_wiktionaries_with_langs(wiktionaries, lemmas_langs);
		
		// concatenating the lemmas lang and the user lang, removing duplicates
		// first the lemmas lang
		
		let preferred_wikts = add_wiktionaries_without_duplicates(lexeme_wikts, default_wiktionaries);
							  
		// queries : for all wiktionaries, 
		// searching titles that exists on the wiki, and generating the list of interwikis
		// (call to all apis, in parralel)
		
		async function query_wiktionaries_for_pages (wikts, lemmas, progress){
			progress = progress ?? (() => {}) ; /* jshint ignore:line */
			let wiki_pages_results = await Promise.all(
						wikts
							.map(wikt_id => site_details[wikt_id])
							.map(launch_treatment_for_wiki(lemmas, pagename_to_languagelinks, progress))
						);
						console.log(wiki_pages_results);
			return wiki_pages_results.filter(wiki => wiki && wiki.links.length>0);
		}
		function add_wiki_pages_to_list(list, links_of_wiki){
			list.append(
				links_of_wiki
					.map(
						wiki => 
							[$("<dt>").append(wiki.wiki.shortName + " : ")].concat(
								wiki.links.map((a, idx) => $("<dd>",{class: "iw_" + (idx+1)}).append(a))
							)
					).flat(1)
			);
		}
		
		var links_of_wiki = await query_wiktionaries_for_pages(preferred_wikts, lemmas);
		
		let list = $('<dl>',{class:["wiktionaries_links"]});
		
		// adding a link to expand the list with all wiktionaries
		
		let other = $("<a>",{text:"[other wiktionaries …]"}).click(
			async () => {
				let rest_of_wikis = add_wiktionaries_without_duplicates(wiktionaries, preferred_wikts);
				var progressBar = new OO.ui.ProgressBarWidget( {
					progress: 0
				} );
				other = other.replaceWith(progressBar.$element);
				try {
					var nb_done = 0;
					let progress = () => { 
						nb_done += 1 ; 
						progressBar.setProgress(nb_done/rest_of_wikis.length * 100);
					};
					let links = await query_wiktionaries_for_pages(rest_of_wikis, lemmas, progress);
					add_wiki_pages_to_list(list, links);
				} catch(error){
					other.replaceWith("Error !");
				}
			}
		);
		
		
		if(links_of_wiki.length == 0){
			$(".gadget-wikt-iw-container").html("Wikt:nothing");
			return;
		}
		// generating the final language links html links
		add_wiki_pages_to_list(list, links_of_wiki);
   
		// installing the language links button
		set_iw_button_content($("<div>",{class:"wikt-iw-container"}).append(other).append(list),"language");
	}).catch(error =>{
		set_iw_button_content(new OO.ui.MessageWidget( {
			type: 'error',
			label: "could not load datas : " + error.toString() 
		} ).$element, "error");
		console.log(error);
		// $(".gadget-wikt-iw-container").html("Wikt:error !");
	});
});