// ==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/* // @downloadURL https://github.com/chumbucket/JishoStuff/raw/main/JishoStuff.user.js // @updateURL https://github.com/chumbucket/JishoStuff/raw/main/JishoStuff.user.js // @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 = '
' + '
' + ''; 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); })();