dict.cc-verbix

Injects verb conjugation tables from Verbix on dict.cc

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 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;
	};
}