dict.cc-verbix

Injects verb conjugation tables from Verbix on dict.cc

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name                dict.cc-verbix
// @namespace	        https://github.com/todeit02/dict.cc_verbix_userscript
// @description	        Injects verb conjugation tables from Verbix on dict.cc
// @grant				GM.xmlHttpRequest
// @grant				GM_xmlhttpRequest
// @include				/^https:\/\/(?:(?:([a-z]){2}-?([a-z]){2})|(www))\.dict\.cc\/\?s=.*/
// @connect				api.verbix.com
// @connect 			raw.githubusercontent.com
// @require 			https://code.jquery.com/jquery-3.3.1.js
// @version 0.0.1.20190210183046
// ==/UserScript==

// choose correct function for Greasemonkey/Tampermonkey
if(typeof GM_xmlhttpRequest !== "function") GM_xmlhttpRequest = GM.xmlHttpRequest;

const tenseNamesUrl = "https://raw.githubusercontent.com/todeit02/dict.cc_verbix_userscript/master/tense_names.json";
const verbixApiUrl = "https://api.verbix.com/conjugator/html";
const verbixTableTemplateUrl = "http://tools.verbix.com/webverbix/personal/template.htm";

const dictWordButtonTableClass = "td7cml";
const dictWordTextTableDataClass = "td7nl";
const dictItemTableLineIdSuffix = "tr";
			
	
const verbixLanguageCodes = 
{
	"de": "deu",
	"en": "eng",
	"da": "dan",
	"es": "spa",
	"fi": "fin",
	"fr": "fra",
	"hu": "hun",
	"is": "isl",
	"it": "ita",
	"la": "lat",
	"nl": "nld",
	"no": "nob",
	"pt": "por",
	"ro": "ron",
	"sv": "swe"
};


const usingTemplateMoodTenses = 
[
	["Indicativo", "Presente"],
	["Indicativo", "Perfecto"],
	["Indicativo", "Imperfecto"],
	["Subjuntivo", "Presente"],
	["Imperativo", ""]
];


let languagePair = [];
let templateHeadingsTranslations = {};
let hoveredWordLink;
let openTooltips = [];

let cursorOnWordLink = false;
let cursorOnTooltip = false;


$(function(){
	languagePair = getLanguagePair();
	loadTemplateHeadingsTranslations();
	linkWordsToVerbix();
});


function linkWordsToVerbix()
{
	let dictItemTableLines = $("div[id='maincontent']").find("tr" + "[id^='" + dictItemTableLineIdSuffix + "']");
	let dictWordLinks = dictItemTableLines.find("a").filter(function(){ return $(this).text().length > 0; });
	
	dictWordLinks.hover(
		(event) =>
		{
			cursorOnWordLink = true;
			hoveredWordLink = $(event.target).closest("a");
			createTooltipIfNoneOpen();
		},
		() =>
		{
			cursorOnWordLink = false;
			removeTooltipIfNotHovered();
		});
}


function createTooltipIfNoneOpen()
{
	if(openTooltips.length > 0) return;
	
	let wordText = $(hoveredWordLink).text();
	let isLeftColumn = $(hoveredWordLink).parent().prev().attr("class") === dictWordButtonTableClass;
	
	let dictLanguage = isLeftColumn ? languagePair[0] :  languagePair[1];
	
	loadVerbixConjugationLists(dictLanguage, wordText);
}


function showTooltip(tenseTablesHtml)
{		
	let $tooltip = $("<br /><div></div>").appendTo(hoveredWordLink);
	openTooltips.push($tooltip);

	$tooltip.hover(() => 
	{
		cursorOnTooltip = true;
	},
	() =>
	{
		cursorOnTooltip = false;
		removeTooltipIfNotHovered();
	});
	
	tenseTablesHtml.forEach(function(tenseTable, index){
		if(index > 0) $tooltip.append("<br />");

		$tooltip.append(tenseTable);
	});
	
	$tooltip.parent().css("position", "relative");
	$tooltip.css({
		"display": "initial",
		"opacity": "0",
		"text-align": "center",
		"padding": "0.5em",
		"border-radius": "6px",
		"position": "absolute",
		"z-index": "1",
		"background-color": "#fff",
		"border": "1px solid black",
		"box-shadow": "0 0 0.5em",
		"height": "25em",
		"overflow": "scroll",
		"left": "99%"
	});
	$tooltip.find("span").css("color", "black");
	$tooltip.animate({"opacity": "1"}, 500);
}


function removeTooltipIfNotHovered()
{
	if(cursorOnWordLink || cursorOnTooltip) return;

	openTooltips.forEach(function($tooltip){
		$tooltip.parent().css("position", "");
		$tooltip.remove();
	});
	openTooltips = [];
}


function loadVerbixConjugationLists(dictLanguage, verb)
{
	if(verbixLanguageCodes[dictLanguage] == null) return;

	let getQuery = "language=" + verbixLanguageCodes[dictLanguage] + "&tableurl=" + verbixTableTemplateUrl + "&verb=" + verb;
	let verbixUrl = verbixApiUrl + '?' + getQuery;
	
	GM_xmlhttpRequest({
		method: "GET",
		url: verbixUrl,
		onload: response => {
			let verbixContent = new DOMParser().parseFromString(response.responseText, "text/html");
			let conjugationTables = [];

			usingTemplateMoodTenses.forEach(function(moodTensePair)
			{
				const mood = moodTensePair[0];
				const translatedMood = translateTemplateWord(dictLanguage, "mood", mood);
				const tense = moodTensePair[1];
				let moodTables = $(".verbtense", verbixContent).filter(isTableOfMood(mood));

				if(moodTensePair[1] != null && moodTensePair[1].length > 0)
				{
					const translatedTense = translateTemplateWord(dictLanguage, "tense", tense);
					const $tenseTable = moodTables.filter(isTableOfTense(tense));
					const $heading = $("<b>" + translatedTense + " (" + translatedMood + ")</b>");

					conjugationTables.push($heading)
					conjugationTables.push($tenseTable);
				}
				else
				{
					const $heading = $("<b>" + translatedMood + "</b>");

					conjugationTables.push($heading)
					conjugationTables.push(moodTables);
				}
			});
			showTooltip(conjugationTables);
		}
	});
}


function getLanguagePair()
{
	let url = window.location.href;
	let subdomainRegex = /^https:\/\/(?:(?:([a-z]{2})-?([a-z]{2}))|(www))\.dict\.cc\/\?s=.*/;
	let subdomain = subdomainRegex.exec(url);
	
	if(subdomain[3]) return ["en", "de"];
	if((subdomain[1] === "de" && subdomain[2] === "en") || (subdomain[2] === "de" && subdomain[1] === "en"))
	{
		return ["en", "de"];
	}
	
	const isGermanWithoutEnglish = (subdomain[1] === "de" || subdomain[2] === "de");
	const isEnglishWithoutGerman = (subdomain[1] === "en" || subdomain[2] === "en");	
	let languages = [];
	
	if(isGermanWithoutEnglish)
	{
		if(subdomain[1] === "de") languages.push(subdomain[2]);
		else languages.push(subdomain[1]);
		
		languages.push("de");
	}
	if(isEnglishWithoutGerman)
	{
		if(subdomain[1] === "en") languages.push(subdomain[2]);
		else languages.push(subdomain[1]);
		
		languages.push("en");
	}
	return languages;
}


function loadTemplateHeadingsTranslations()
{
	$.getJSON(tenseNamesUrl, data =>
	{
		templateHeadingsTranslations = data;
	});
}


function translateTemplateWord(dictLanguage, verbAspect, word)
{
	verbAspect += 's';
	const fallbackTranslations = templateHeadingsTranslations["es"];
	const usingTranslations = templateHeadingsTranslations[dictLanguage] || fallbackTranslations;
	const usingAspect = usingTranslations[verbAspect] || fallbackTranslations[verbAspect];
	
	return usingAspect[word];
}


function isTableOfMood(templateMoodName)
{
	return function()
	{
		const $moodTableDataInMain = $(this).parents("td").eq(1);
		const $moodTableRowInMain = $moodTableDataInMain.parent();
		const $mainTable = $moodTableRowInMain.parents("table").first();
		const moodColIndexInMain = $moodTableDataInMain.index();
		const moodRowIndexInMain = $moodTableRowInMain.index();
		const heading = $("tr", $mainTable).eq(moodRowIndexInMain - 1).children().eq(moodColIndexInMain).text();

		return (heading === templateMoodName);
	}
}
	

function isTableOfTense(templateTenseName)
{
	return function()
	{
		let parentText = $(this).parent().text();		
		while(parentText.charAt(0) === '\r' || parentText.charAt(0) === '\n')
		{
			parentText = parentText.substr(1);
		}		
		return parentText.substring(0, templateTenseName.length) === templateTenseName;
	};
}