您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
入力されている全文をポップアップ表示し、ポップアップ内で編集可能。改行表示の設定を追加。文字数カウンターを追加。備考欄にヘルプを追加。
// ==UserScript== // @name 備考欄の改良 // @namespace http://tampermonkey.net/ // @version 1.11 // @description 入力されている全文をポップアップ表示し、ポップアップ内で編集可能。改行表示の設定を追加。文字数カウンターを追加。備考欄にヘルプを追加。 // @license MIT // @match *://plus-nao.com/forests/*/mainedit/* // @match *://plus-nao.com/forests/*/registered_mainedit/* // @grant none // @run-at document-end // ==/UserScript== (function() { 'use strict'; const helpLinkHTML = ` (=> <a href="http://tk2-217-18298.vs.sakura.ne.jp/projects/newproducts/wiki/%E5%82%99%E8%80%83%E6%AC%84%E3%83%98%E3%83%AB%E3%83%97" target="_blank">ヘルプ</a> ) `; const MAX_LENGTH = 255; const style = document.createElement('style'); style.textContent = ` .cursor-warning { color: red; } .space-settings-button { position: absolute; font-size: 12px; top: -12px; right: -10px; background: transparent; color: white; border: none; border-radius: 5px; padding: 5px 5px; cursor: pointer; } .space-settings-popup { position: absolute; top: 50px; right: 10px; background: white; border: 1px solid #ccc; border-radius: 5px; padding: 10px; z-index: 1000; display: none; } .space-settings-popup label { display: block; } `; document.head.appendChild(style); window.addEventListener('load', function() { const remarksHeader = [...document.querySelectorAll('th[scope="row"]')].find(th => th.textContent.includes("備考")); if (remarksHeader) { const helpLinkSpan = document.createElement('span'); helpLinkSpan.innerHTML = helpLinkHTML; remarksHeader.appendChild(helpLinkSpan); } const inputField = document.getElementById('TbMainproduct備考'); if (!inputField) return; const wrapperDiv = document.createElement('div'); wrapperDiv.style.position = 'relative'; inputField.parentNode.insertBefore(wrapperDiv, inputField); wrapperDiv.appendChild(inputField); inputField.style.width = 'calc(100% - 60px)'; const popupStyle = ` position: absolute; background-color: white; border: 2px solid #ccc; border-radius: 5px; padding: 4px 10px; z-index: 1000; display: none; overflow: auto; white-space: pre-wrap; word-wrap: break-word; box-sizing: border-box; width: calc(100% - 60px); `; const popup = document.createElement('div'); popup.className = 'remarks-popup'; popup.style.cssText = popupStyle; popup.contentEditable = true; wrapperDiv.appendChild(popup); const cursorPosition = createCursorPosition(); wrapperDiv.appendChild(cursorPosition); const settingsButton = document.createElement('button'); settingsButton.className = 'space-settings-button'; settingsButton.textContent = '⚙️'; wrapperDiv.appendChild(settingsButton); const settingsPopup = document.createElement('div'); settingsPopup.className = 'space-settings-popup'; settingsPopup.innerHTML = ` <label title="チェックを入れると、入力欄の半角スペースをポップアップ内で改行として表示します。"> <input type="checkbox" id="spaceAsNewlineToggle"> 半角スペースを改行として表示 </label> `; wrapperDiv.appendChild(settingsPopup); const spaceAsNewlineToggle = document.getElementById('spaceAsNewlineToggle'); let spaceAsNewline = localStorage.getItem('spaceAsNewline') === 'true'; spaceAsNewlineToggle.checked = spaceAsNewline; settingsButton.addEventListener('click', (event) => { event.preventDefault(); settingsPopup.style.display = settingsPopup.style.display === 'block' ? 'none' : 'block'; }); spaceAsNewlineToggle.addEventListener('change', () => { spaceAsNewline = spaceAsNewlineToggle.checked; localStorage.setItem('spaceAsNewline', spaceAsNewline); updatePopup(); }); function updatePopup() { if (inputField === document.activeElement && inputField.value.trim() !== '') { popup.textContent = spaceAsNewline ? inputField.value.replace(/ /g, '\n') : inputField.value; popup.style.display = 'block'; } else { popup.style.display = 'none'; } } function updateCursorPosition(focused, customPosition = null) { let position; let totalLength; if (focused && inputField === document.activeElement) { position = inputField.selectionStart; totalLength = inputField.value.length; } else if (focused && popup === document.activeElement) { const selection = window.getSelection(); position = customPosition !== null ? customPosition : selection.anchorOffset; totalLength = popup.textContent.length; } else { position = 0; totalLength = inputField.value.length; } cursorPosition.textContent = focused ? `${position}/${totalLength}` : `${totalLength}`; if (totalLength > MAX_LENGTH) { cursorPosition.classList.add('cursor-warning'); } else { cursorPosition.classList.remove('cursor-warning'); } } updateCursorPosition(false); function createCursorPosition() { const span = document.createElement('span'); span.style.marginLeft = '3px'; span.style.fontSize = '11px'; span.style.verticalAlign = 'middle'; return span; } function validatePopupInput() { let currentText = popup.textContent; currentText = currentText.replace(/\n/g, ' '); if (currentText.length > MAX_LENGTH) { currentText = currentText.substring(0, MAX_LENGTH); popup.textContent = currentText; inputField.value = currentText; } } let isComposing = false; popup.addEventListener('compositionstart', function() { isComposing = true; }); popup.addEventListener('compositionupdate', function(event) { const updatedText = popup.textContent.replace(/\n/g, ' '); inputField.value = updatedText; setTimeout(() => { updateCursorPosition(true); }, 0); }); popup.addEventListener('compositionend', function(event) { isComposing = false; const updatedText = popup.textContent.replace(/\n/g, ' '); inputField.value = updatedText; setTimeout(() => { updateCursorPosition(true); }, 0); }); inputField.addEventListener('blur', function() { updateCursorPosition(false); }); inputField.addEventListener('keyup', function() { updateCursorPosition(true); }); inputField.addEventListener('click', function() { updateCursorPosition(true); }); inputField.addEventListener('focus', updatePopup); inputField.addEventListener('input', function() { updatePopup(); updateCursorPosition(true); }); let shouldRedraw = false; popup.addEventListener('mouseup', () => { updateCursorPosition(true); }); popup.addEventListener('focus', function() { updateCursorPosition(true); }); popup.addEventListener('blur', function() { if (popup.textContent.length > MAX_LENGTH) { alert(`入力可能な文字数を超えています。256字以降は切り捨てられます。`); } const updatedText = popup.textContent.replace(/\n/g, ' '); popup.textContent = updatedText; inputField.value = updatedText; validatePopupInput(); updateCursorPosition(false); }); popup.addEventListener('keydown', function (event) { const selection = window.getSelection(); const range = selection.getRangeAt(0); const cursorOffset = range.startOffset; const isPopupActive = document.activeElement === popup; const targetElement = isPopupActive ? popup : inputField; const isLastCharacter = cursorOffset === targetElement.textContent.length; if (event.key === 'Enter') { let beforeCursor = targetElement.textContent.slice(0, cursorOffset); let afterCursor = targetElement.textContent.slice(cursorOffset); if (isLastCharacter && !targetElement.textContent.endsWith('\n')) { if (isPopupActive) { targetElement.textContent = beforeCursor + '\n\n' + afterCursor; } else { targetElement.value = beforeCursor + ' ' + afterCursor; } } else { if (isPopupActive) { targetElement.textContent = beforeCursor + '\n' + afterCursor; } else { targetElement.value = beforeCursor + ' ' + afterCursor; } } const newRange = document.createRange(); const firstChild = targetElement.firstChild; if (firstChild && firstChild.nodeType === 3) { const newCursorPosition = beforeCursor.length + (isPopupActive ? 1 : 0); newRange.setStart(firstChild, newCursorPosition); newRange.collapse(true); selection.removeAllRanges(); selection.addRange(newRange); setTimeout(() => updateCursorPosition(isPopupActive, newCursorPosition), 0); } event.preventDefault(); } else { setTimeout(() => updateCursorPosition(isPopupActive), 0); } }); popup.addEventListener('input', () => { const selection = window.getSelection(); const range = selection.getRangeAt(0); const startOffset = range.startOffset; const startNode = range.startContainer; const text = popup.textContent; const updatedText = text.replace(/\n/g, ' '); inputField.value = updatedText; updateCursorPosition(true); const newRange = document.createRange(); newRange.setStart(startNode, Math.min(startOffset, text.length)); newRange.collapse(true); selection.removeAllRanges(); selection.addRange(newRange); }); popup.addEventListener('click', function() { updateCursorPosition(true); }); document.addEventListener('click', event => { if (window.getSelection().type === "Range") { return; } if (!popup.contains(event.target) && !inputField.contains(event.target)) { popup.style.display = 'none'; inputField.blur(); } }); }); })();