Scrapbox Dynamic Macro
キーバインドは
ctrl + space
にしている (変更する場合は
ここを変える)
動作確認済み環境
macOS High Sierra
Google Chrome
USキー配列
import.jsimport '/api/code/customize/Scrapbox_Dynamic_Macro/script.js'9
バグ等ありましたら報告お願いします!
下のように文字が消えてしまう不具合を発見しました

script.jsconst 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.jsconst dynamicMacro = () => {
if (eventStack.length) {
dmacro = searchMacro();
} else {
execMacro(dmacro);
}
};
script.jsconst 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.jsconst 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.jsconst fireInsert = (str) => {
document.execCommand('insertText', null, str);
}