generated at
Scrapbox Dynamic Macro


増井俊之先生のDynamic MacroをScrapbox上で実装してみた
キーバインドは ctrl + space にしている (変更する場合はここを変える)
vim . キーみたい

動作確認済み環境
macOS High Sierra
Google Chrome
USキー配列


import.js
import '/api/code/customize/Scrapbox_Dynamic_Macro/script.js'9

バグ等ありましたら報告お願いします!

下のように文字が消えてしまう不具合を発見しました基素

hr

script.js
const specificKeys = ['Control', 'Alt', 'Shift', 'Meta', 'Unidentified']; const operateKeys = ['Enter', 'Backspace', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight']; let cursor = $('#text-input')[0]; let enterPressed = false; let eventStack = []; let dmacro = null; let macroRunning = false;

keydown のEventListener
ctrl + A などの複合的なカーソル操作を受け取る
script.js
$('#text-input').on('keydown', (e) => { if (e.ctrlKey && e.key == ' ') { dynamicMacro(); return; } // if (dmacro) { // dmacro = null; // stackEvent = []; // } if (e.key == 'Enter') enterPressed = true; if (!e.key || specificKeys.includes(e.key)) return; if (!operateKeys.includes(e.key) && !e.ctrlKey && !e.altKey && !e.shiftKey && !e.metaKey) return; eventStack.push(JSON.stringify({ keyCode : e.keyCode, ctrlKey : e.ctrlKey, altKey : e.altKey, shiftKey : e.shiftKey, metaKey : e.metaKey })); });

文字列入力へのeventListener
半角入力はすべて受け取る
全角入力は確定時のみに一括で受け取る
script.js
$('#text-input').on('input', ({ originalEvent }) => { // if (dmacro) { // dmacro = null; // stackEvent = []; // } if (originalEvent.inputType != 'insertText' && !enterPressed) { enterPressed = false; return; } if (originalEvent.data != 'getIndexByTitleLc' && !macroRunning) { eventStack.push(originalEvent.data); } enterPressed = false; });

script.js
const dynamicMacro = () => { if (eventStack.length) { dmacro = searchMacro(); } else { execMacro(dmacro); } };

script.js
const searchMacro = () => { let macroArray = []; let candidate = []; let originalStackLength = eventStack.length; let latestEvent = eventStack.pop(); console.log(latestEvent); let idx = eventStack.indexOf(latestEvent); while (idx != -1) { candidate.push(idx); idx = eventStack.indexOf(latestEvent, idx + 1); } macroArray.unshift(latestEvent); for(let i = 1; i < 30; i++) { let previousCandidate = candidate.slice(); let newerEvent = eventStack.pop(); for (let j = 0; j < candidate.length; j++) { if (candidate[j]+i == originalStackLength) { macroArray.unshift(newerEvent); execMacro(macroArray); return macroArray; } if (candidate[j]-i >= 0 && eventStack[candidate[j]-i] == newerEvent) continue; candidate.splice(j, 1); j--; } console.log(candidate); console.log(previousCandidate); if (candidate.length == 0) { if (previousCandidate.length == 1) { if(previousCandidate[0] + i + 1 == originalStackLength) { execMacro(macroArray); console.log(123); } else { let remain = eventStack.slice(previousCandidate[0]+1); remain.push(newerEvent); execMacro(remain); console.log(456); } console.log(previousCandidate[0]-i+1); macroArray = eventStack.slice(previousCandidate[0]-i+1); macroArray.push(newerEvent); eventStack = []; return macroArray; } else { console.log(previousCandidate); let notYetEnteredIndex = Math.max(...previousCandidate)+1; execMacro(eventStack.slice(notYetEnteredIndex)); console.log(0 + ' ' + notYetEnteredIndex); console.log(eventStack); eventStack = []; return macroArray; } } else { macroArray.unshift(newerEvent); } } eventStack = []; return []; }

引数の配列のevent (疑似objectと文字列) の発火を行う
script.js
const execMacro = (macro) => { setTimeout(() => { macroRunning = true; for (let i = 0; i < macro.length; i++) { let e = macro[i]; if (e.startsWith('{') && e.endsWith('}')) { // KeyboardEvent fireKeydown(e); } else {// InputEvent fireInsert(e); } } macroRunning = false; }, 50); }

KeyboardEvent の発火ができる
script.js
const fireKeydown = (event) => { let { keyCode, ctrlKey, altKey, shiftKey, metaKey } = JSON.parse(event); if (keyCode == 13) { // Enter fireInsert('\n'); } else if (keyCode == 'Backspace') { document.execCommand('forwardDelete', null, null); } else { let event = document.createEvent('Events'); event.initEvent('keydown', true, true); event.keyCode = event.which = keyCode; event.ctrlKey = ctrlKey; event.altKey = altKey; event.shiftKey = shiftKey; event.metaKey = metaKey; cursor.dispatchEvent(event); } }

InputEvent の発火ができる
実際には document.execCommand なので疑似的
script.js
const fireInsert = (str) => { document.execCommand('insertText', null, str); }