Jisho stuff

Search the radical list by labels that you define.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Jisho stuff
// @namespace    https://github.com/chumbucket/JishoStuff
// @version      0.4
// @description  Search the radical list by labels that you define.
// @author       chumbucket
// @match        https://jisho.org/*
// @grant        none
// ==/UserScript==

(function () {
	'use strict';
	const extraFunctions = function () {
		const hideFurigana = function () {
			if (-1 == location.href.indexOf('%23sentences')) {
				return;
			}
			var styleElement = document.createElement('style');
			styleElement.setAttribute('type', 'text/css');
			styleElement.innerHTML =
        '.furigana{opacity:0!important}' +
        '.furigana:hover{opacity:1!important}' +
        '.english{opacity:0!important}' +
        '.english:hover{opacity:1!important}';
			document.body.appendChild(styleElement);
		};
		hideFurigana();
		const searchSelectedWord = function (e) {
			if (13 == e.keyCode) {
				var selectedWord = window.getSelection().toString();
				if (selectedWord) {
					window.open('https://jisho.org/search/' + selectedWord);
				}
			}
		};
		window.addEventListener('keyup', searchSelectedWord);
	};
	const $ = window.jQuery || null;
	if (!$) {
		console.error('TM JishoStuff: jQuery is undefined');
		return;
	}
	const radicalInput = new window.RadicalInput() || null;
	if (!radicalInput) {
		console.error('TM JishoStuff: RadicalInput is undefined');
		return;
	}
	let radTable = $('.radical_table');
	let radicals = $('.radical', '.radical_table');
	radTable[0].addEventListener('click', function (evt) {
		let rad = $(evt.target);
		if (rad.hasClass('radical')) {
			let radindex = radicals.index(rad);
			if (rad.hasClass('selected')) {
				$('.search-term').each(function () {
					if (radindex == $(this).data('index')) {
						$(this).remove();
					}
				});
			}
		}
	});
	const defaultRadLabels =
    '一;one,|;line,丶;dot,ノ;no,乙;second rank,亅;putter,二;two,亠;lid,人;person,⺅;person left,' +
    '𠆢;roof,儿;legs,入;enter,ハ;eight,丷;together,冂;moustache,冖;bracket,冫;cold left,几;table,凵;open box,' +
    '刀;sword,⺉;sword right,力;power,勹;wrap,匕;hi,匚;side box,十;ten,卜;to,卩;fingerprint,厂;cliff,厶;mu,' +
    '又;crotch,マ;ma,九;round,ユ;yu,乃;sharp butt,𠂉;rifle,⻌;motion,口;mouth,囗;big mouth,土;dirt,' +
    '士;samurai,夂;each,夕;evening,大;big,女;woman,子;child,宀;crown,寸;stick,小;small,⺌;mohawk,' +
    '尢;crooked big,尸;corpse,屮;mountain tail,山;mountain,川;river,巛;river flow,工;construction,' +
    '已;self,巾;cloth,干;dry,幺;eazy,广;cave,廴;stretch,廾;letter h,弋;ceremony,弓;bow,ヨ;yo,' +
    '彑;reciprocal,彡;short hair,彳;go left,⺖;heart left,⺘;hand left,⺡;water left,⺨;animal,' +
    '⺾;flower top,⻏;boston right,⻖;boston left,也;scorpion,亡;deceased,及;reach,久;long time,' +
    '⺹;dig,心;heart,戈;spear,戸;door,手;hand,支;branch,攵;director,文;sentence,斗;spice rack,斤;axe,方;direction,' +
    '无;crooked heaven,日;sun,曰;flat sun,月;moon,木;tree,欠;lack,止;stop,歹;death,殳;nurse,比;compare,' +
    '毛;fur,氏;surname,气;steam,水;water,火;fire,⺣;fire bottom,爪;nail,父;father,爻;dos equis,' +
    '爿;left side,片;right side,牛;cow,犬;dog,⺭;ne,王;king,元;origin,井;hashtag,勿;rib,尤;crooked dog,' +
    '五;five,屯;fort,巴;nerd,毋;every,玄;deep,瓦;tile,甘;sweet,生;life,用;use,田;rice field,疋;incorrect,' +
    '疒;sick,癶;tent,白;white,皮;skin,皿;plate,目;eye,矛;beforehand,矢;arrow,石;stone,示;indicate,' +
    '禸;cow goatee,禾;wheat,穴;hole,立;stand,⻂;duck,世;world,巨;giant,冊;books,母;mother,⺲;net,牙;fang,' +
    '瓜;melon,竹;bamboo,米;rice,糸;thread,缶;can,羊;sheep,羽;feather,而;comb,耒;branch tree,耳;ear,' +
    '聿;brush,肉;meat,自;myself,至;climax,臼;mortar,舌;tongue,舟;boat,艮;good,色;color,虍;tiger,虫;bug,' +
    '血;blood,行;go,衣;clothes,西;west,臣;slave,見;see,角;corner,言;say,谷;valley,豆;bean,豕;pig,豸;snake,' +
    '貝;shellfish,赤;red,走;run,足;foot,身;somebody,車;car,辛;spicy,辰;shake,酉;sake,釆;come,' +
    '里;village,舛;dance,麦;barley,金;gold,長;long,門;gate,隶;extend,隹;turkey,雨;rain,青;blue,非;un,奄;big dragon,' +
    '岡;hill,免;excuse,斉;equal,面;surface,革;leather,韭;leek,音;sound,頁;page,風;wind,飛;fly,食;eat,首;neck,香;perfume,' +
    '品;goods,馬;horse,骨;bone,高;high,髟;hair,鬥;broken gate,鬯;herbs,鬲;tripod,鬼;demon,竜;dragon,韋;tanned leather,' +
    '魚;fish,鳥;bird,鹵;salt,鹿;deer,麻;hemp,亀;turtle,啇;drip,黄;yellow,黒;black,黍;millet,黹;sewing,無;nothing,歯;tooth,' +
    '黽;green frog,鼎;kettle,鼓;drum,鼠;mouse,鼻;nose,齊;alike,龠;flute';
	const initRadKeys = function () {
		let savedLabels = window.localStorage
			? localStorage.getItem('radLabels')
			: null;
		let radKeys = {};
		if (!savedLabels) {
			let radDef = defaultRadLabels.split(',');
			radDef.forEach(function (rad, index) {
				let radk = rad.split(';');
				if (radk.length < 2) {
					return;
				}
				radKeys[radk[1]] = index;
			});
		} else {
			radKeys = JSON.parse(savedLabels);
		}
		return radKeys;
	};
	let radKeys = initRadKeys();
	const saveRadLabels = function () {
		if (!window.localStorage) {
			return false;
		}
		try {
			localStorage.setItem('radLabels', JSON.stringify(radKeys));
			return true;
		} catch (e) {
			return false;
		}
	};
	saveRadLabels() || console.error('Error saving labels to localStorage');
	const liForRadKey = function (key) {
		let tableindex = radKeys[key];
		if (void 0 == tableindex) {
			return false;
		}
		return radicals[tableindex];
	};
	Object.keys(radKeys).forEach(function (key) {
		let radEl = liForRadKey(key);
		radEl.title = `*${key}`;
		$(radEl).data('label', key);
	});
	$('[title]').tooltip();
	const createRadSearch = function () {
		let input = document.createElement('div');
		input.id = 'rad-searchbar';
		input.setAttribute('contenteditable', 'true');
		let showlessdiv = $('.show_less', '#radical_area')[0];
		showlessdiv.insertAdjacentElement('afterend', input);
		return input;
	};
	Object.defineProperty(HTMLElement.prototype, 'value', {
		get: function () {
			return this.textContent;
		},
		set: function (x) {
			this.textContent = x;
		},
	});
	const createEditButton = function () {
		let radTable = $('.radical_table');
		let button = document.createElement('div');
		button.textContent = 'Edit';
		button.setAttribute('class', 'icon edit-label');
		radTable.prepend(button);
		return button;
	};
	const makeEditOverlay = function () {
		var editContainer = document.createElement('div');
		editContainer.id = 'edit-container';
		$(editContainer).click(function () {
			$(this).css('display', 'none');
			$('.save-result').text('');
		});
		let editbody = document.createElement('div');
		let bodyHtml =
      '<div class="rad-text"></div>' +
      '<div class="save-result"></div>' +
      '<input class="edit-input">';
		editbody.id = 'edit-body';
		$(editbody).click(function (evt) {
			evt.stopPropagation();
		});
		$(editbody).append(bodyHtml);
		$(editContainer).append(editbody);
		$(document.body).prepend(editContainer);
		$('.edit-input').data('radindex', '');
		$('.edit-input').data('label', '');
	};
	const makeRadOverlays = function () {
		$(radicalInput.radicals).each(function () {
			let radoverlay = document.createElement('div');
			radoverlay.setAttribute('class', 'rad-overlay');
			let clickedRadical = this;
			$(radoverlay).click(function () {
				$('#edit-container').css('display', 'flex');
				editRadLabel(clickedRadical);
			});
			$(this).prepend(radoverlay);
		});
	};
	const editRadLabel = function (rad) {
		let radLabel = $(rad).data('label');
		let radIndex = $(radicals).index(rad);
		if (radLabel) {
			$('.edit-input').val(radLabel);
		} else {
			$('.edit-input').val('');
		}
		$('.edit-input').data('radindex', radIndex);
		$('.edit-input').data('label', radLabel);
		$('.rad-text').text($(rad).text());
	};
	const showRadOverlays = function () {
		if (editMode) {
			$('.rad-overlay').css('display', 'inherit');
		} else {
			$('.rad-overlay').css('display', 'none');
		}
	};
	const makeSpanWithTerm = function (searchterm) {
		let span = document.createElement('span');
		span.setAttribute('class', 'search-term');
		span.textContent = searchterm[0];
		span = $(span);
		span.attr('data-index', radKeys[searchterm[0]]);
		span.click(
			(function (me) {
				return function () {
					let radical = radInfoFromTerm(me.text());
					radicalInput.selected_radicals = radicalInput.selected_radicals.subtract(
						radical.text
					);
					if (0 == radicalInput.selected_radicals.length) {
						radicalInput.reset();
					} else {
						radicalInput.getKanji();
					}
					radical.element.removeClass('selected');
					radical.text;
					radicalInput.selected_radicals;
					me.remove();
				};
			})(span)
		);
		let resultsarea = $('.results', '#radical_area');
		resultsarea.append(span);
		return span;
	};
	let radSearchBar = createRadSearch();
	let editButton = createEditButton();
	makeRadOverlays();
	makeEditOverlay();
	let editMode = false;
	$(editButton).on('click', function (evt) {
		evt.preventDefault();
		if (editMode) {
			editMode = false;
			$(radSearchBar).attr('contenteditable', 'true');
			$(this).css({ 'background-color': 'white', color: '#222' });
			$('.radical_table').css('background', '');
		} else {
			editMode = true;
			$('.results .search-term').remove();
			radicalInput.reset();
			$(radSearchBar).attr('contenteditable', 'false');
			$(this).css({ 'background-color': '#555', color: 'white' });
			$('.radical_table').css({
				background: 'none',
				'background-color': '#edf9ff',
			});
		}
		showRadOverlays();
	});
	let showSaveResultText = function (resulttext, bool) {
		if (bool) {
			$('.save-result').css('color', '#2ecb2e');
		} else {
			$('.save-result').css('color', 'red');
		}
		$('.save-result').text(resulttext);
	};
	$('.edit-input').on('keydown', function (e) {
		if (13 == e.keyCode) {
			if ('' == $(this).val()) {
				return;
			}
			e.preventDefault();
			let oldLabel = $(this).data('label');
			let newLabel = $(this).val();
			newLabel = newLabel.replace(/\s*$/, '');
			if (newLabel in radKeys) {
				showSaveResultText('This label is already taken');
				return;
			} else {
				delete radKeys[oldLabel];
				radKeys[newLabel] = $(this).data('radindex');
				$(this).data('label', newLabel);
				let radTableItem = radicals[radKeys[newLabel]];
				$(radTableItem).data('label', newLabel);
				$(radTableItem).attr('title', `*${newLabel}`);
				$(radTableItem).tooltip();
				if (saveRadLabels()) {
					showSaveResultText('Saved', true);
				} else {
					showSaveResultText('Couldn\'t save to local storage');
				}
				radKeys[newLabel];
			}
		}
	});
	const buttontoggle = function (_this) {
		return function () {
			if (_this.active) {
				return _this.deactivate();
			} else {
				return _this.activate();
			}
		};
	};
	radicalInput.table.off();
	radicalInput.area.off();
	radicalInput.button.off();
	radicalInput.list.off();
	radicalInput.setupEvents();
	radicalInput.resetRadicalsButton.off();
	radicalInput.button.on('click', buttontoggle(radicalInput));
	radicalInput.resetRadicalsButton.on(
		'click',
		(function (_this) {
			return function () {
				_this.reset();
				$('.results .search-term').remove();
			};
		})(radicalInput)
	);
	const updateOptionsArray = function (terms) {
		if (0 == terms.length) {
			return [];
		}
		let optionsarray = [];
		let searchTerm = terms.last();
		Object.keys(radKeys).forEach(function (key) {
			if (0 == searchTerm.length) {
				return;
			}
			let radTableItem = radInfoFromTerm(key);
			if (
				0 == key.indexOf(searchTerm) &&
        radTableItem.element.hasClass('available')
			) {
				optionsarray.push(key);
			} else {
				return;
			}
		});
		return optionsarray.sort();
	};
	const highlightRadicals = function (arr) {
		if (0 == arr.length) {
			return;
		}
		for (var i = 0; i < arr.length; i++) {
			let radTableItem = liForRadKey(arr[i]);
			radTableItem = $(radTableItem);
			if (
				radTableItem.hasClass('selected') ||
        !radTableItem.hasClass('available')
			) {
				continue;
			}
			radTableItem.addClass('selected');
		}
	};
	const clearHighlightRadicals = function (arr) {
		if (0 == arr.length) {
			return;
		}
		for (var i = 0; i < arr.length; i++) {
			let radTableItem = radInfoFromTerm(arr[i]).element;
			let radk = radInfoFromTerm(arr[i]).text;
			if (-1 != radicalInput.selected_radicals.indexOf(radk)) {
				continue;
			}
			if (radTableItem && radTableItem.hasClass('selected')) {
				radTableItem.removeClass('selected');
			}
		}
	};
	const radInfoFromTerm = function (term) {
		let radTableItem = liForRadKey(term);
		if (!radTableItem) {
			return false;
		}
		radTableItem = $(radTableItem);
		let radk = radTableItem.data('radk') || radTableItem.text();
		return { element: radTableItem, text: radk };
	};
	let lastAutoCompOpts = [];
	let currentSearch = '';
	let lastCaretLocation = 0;
	let optionsindex = 0;
	const radSearchInput = function (e) {
		let selection = window.getSelection();
		lastCaretLocation = selection.anchorOffset;
		if ((e.keyCode >= 65 && e.keyCode <= 90) || 32 == e.keyCode) {
			optionsindex = 0;
			if (!e.metaKey) {
				e.preventDefault();
			}
			if (32 == e.keyCode) {
				currentSearch += ' ';
			} else {
				currentSearch += e.key;
			}
		}
		let terms = [this.value];
		let optionsarray = updateOptionsArray([currentSearch]);
		if (13 == e.keyCode) {
			e.preventDefault();
			e.type;
			clickRadical(terms);
			this.value = '';
			currentSearch = '';
			terms = [];
			optionsarray = [];
			optionsindex = 0;
		}
		if (188 == e.keyCode) {
			e.type;
			clickRadical(terms);
			this.value = '';
			currentSearch = '';
			terms = [];
			optionsarray = [];
			optionsindex = 0;
		}
		if (8 == e.keyCode) {
			optionsindex = 0;
			e.preventDefault();
			e.type, radicalInput.selected_radicals;
			if (currentSearch.length > 0) {
				currentSearch = currentSearch.substr(0, currentSearch.length - 1);
				optionsarray = updateOptionsArray([currentSearch]);
			} else {
				this.value = '';
				currentSearch = '';
				optionsarray = [];
			}
		}
		if (9 == e.keyCode) {
			e.preventDefault();
			e.type;
			if (optionsarray.length) {
				optionsindex++;
				if (optionsindex > optionsarray.length - 1) {
					optionsindex = 0;
				}
			}
		}
		clearHighlightRadicals(lastAutoCompOpts);
		if (optionsarray.length > 0) {
			this.value = optionsarray[optionsindex];
			selection.collapse(this.childNodes[0], currentSearch.length);
		} else {
			this.value = currentSearch;
			selection.collapse(this.childNodes[0], currentSearch.length);
		}
		if ('' == this.value) {
			selection.collapse(this, 0);
		}
		lastAutoCompOpts = optionsarray;
		highlightRadicals(optionsarray);
		currentSearch.length;
	};
	radSearchBar.addEventListener('keydown', radSearchInput);
	radSearchBar.addEventListener('keyup', function (e) {
		if (
			(e.keyCode >= 65 && e.keyCode <= 90) ||
      32 == e.keyCode ||
      8 == e.keyCode ||
      9 == e.keyCode
		) {
			if (!e.metaKey) {
				e.preventDefault();
			}
		}
		if (13 == e.keyCode) {
			e.preventDefault();
		}
	});
	const clickRadical = function (search) {
		let searchterms = search;
		searchterms = searchterms.subtract('');
		searchterms.forEach(function (term) {
			let radical = radInfoFromTerm(term);
			if (!radical) {
				return;
			}
			if (!radical.element.hasClass('available')) {
				return;
			}
			radical.element.addClass('selected');
			if (-1 == radicalInput.selected_radicals.indexOf(radical.text)) {
				radicalInput.selected_radicals.push(radical.text);
				makeSpanWithTerm(searchterms);
			}
		});
		if (0 != radicalInput.selected_radicals.length) {
			let fetchResult = radicalInput.getKanji();
		}
		radicalInput.selected_radicals;
	};
	let globalStyle = document.createElement('style');
	globalStyle.innerHTML =
    '#rad-searchbar{display:block;font-size:22px;background-color:white;outline:none;width:70%;' +
    'height:33px;border-radius:3px;margin-bottom:10px;margin-top:10px;padding-left:5px;' +
    'padding-right:5px;}.icon.edit-label{margin:0px;padding:0px;padding-top:2px;padding-left:5px;' +
    'padding-right:5px;float:left;cursor:pointer;border-radius:5px;height:30px;}.icon.edit-label:hover{background-color:#555!important;' +
    'color:#fff!important;}#edit-container{width:100%;height:100%;background-color:rgba(0,0,0,0.5);' +
    'position:fixed;z-index:1000;top:0px;left:0px;display:none;align-items:center;justify-content:center;' +
    '}#edit-body{display:flex;flex-direction:column;align-items:center;justify-content:center;' +
    'width:200px;height:200px;border-radius:10px;background-color:white;}.edit-input{width:75%!important;' +
    'border-radius:5px!important;text-align:center;}.rad-text{font-size:70px;text-align:center;' +
    'cursor:default;}.save-result{font-size:12px!important;height:18px;}.rad-overlay{position:absolute;' +
    'width:32px;height:32px;margin-left:-2px;margin-top:-2px;display:none;}.search-term{background-color:gray;' +
    'color:white;border-radius:10px;padding:5px;cursor:pointer;margin-right:5px;}.search-input{display:block;' +
    'width:auto;background-color:white;outline:none;}';
	$('head').append(globalStyle);
})();