クリックでON・OFFできるチェックボックスUserScript
変更点
記号の後に半角スペースを入れる
200ms or 記号入力後に記号のcursorをpointerにする
範囲選択のポップアップから記号を挿入
code:script.js
class KeydownEvent {
constructor() {
this.textArea = document.getElementById("text-input");
this.event = document.createEvent("UIEvent");
this.event.initEvent("keydown", true, true);
}
dispatch(
keyCode,
withShift = false,
withCtrl = false,
withAlt = false,
withCommand = false
) {
this.event.keyCode = keyCode;
this.event.shiftKey = withShift;
this.event.ctrlKey = withCtrl;
this.event.altKey = withAlt;
this.event.metaKey = withCommand;
this.textArea.dispatchEvent(this.event);
}
}
function isFirstElementChild(element) {
return element.parentNode.firstElementChild === element;
}
function getCursorLineString() {
return document.querySelector(".lines div.line.cursor-line").textContent;
}
function isCharSpan(element, targetCharList) {
return (
element.tagName === "SPAN" &&
targetCharList.includes(element.textContent) &&
element.classList.value.split(" ").some((value) => /^c\-\d+$/.test(value))
);
}
function writeText(text) {
const textArea = document.getElementById("text-input");
textArea.value = text;
textArea.dispatchEvent(
new InputEvent("input", { bubbles: true, cancelable: true })
);
}
const checkboxSetList = "⬜", "✅";
function toCursorPointer() {
Array.from(document.querySelectorAll("span.char-index"))
.filter(el => checkboxSetList.some(list => list.includes(el.textContent)))
.forEach(el => {
el.style.cursor = "pointer";
});
}
setInterval(toCursorPointer, 200);
scrapbox.PopupMenu.addButton({
title: "make todo",
onClick: text => {
return ${checkboxSetList[0][0]} ${text};
}
});
(() => {
const allBoxes = checkboxSetList.reduce(
(accu, current) => accu.concat(current),
[]
);
const startsWithBoxReg = new RegExp("^\\s*(" + allBoxes.join("|") + ")");
const targetProject = scrapbox.Project.name;
// ボックスクリックでオンオフする
$("#app-container").off(click.toggleCheckBox_${targetProject}, ".lines");
$("#app-container").on(
click.toggleCheckBox_${targetProject},
".lines",
async (event) => {
if (scrapbox.Project.name !== targetProject) {
$("#app-container").off(
click.toggleCheckBox_${targetProject},
".lines"
);
return;
}
const target = event.target;
if (!isFirstElementChild(target) || !isCharSpan(target, allBoxes)) return;
await new Promise((resolve) => setTimeout(resolve, 30));
let lineString;
try {
lineString = getCursorLineString();
} catch (err) {
console.log(err);
return;
}
if (!startsWithBoxReg.test(lineString)) return;
const targetX = target.getBoundingClientRect().left;
const cursorX = document
.getElementsByClassName("cursor")0 .getBoundingClientRect().left;
const keydownEvent = new KeydownEvent();
if (cursorX <= targetX) {
keydownEvent.dispatch(39); // →
}
keydownEvent.dispatch(8); // Backspace
const newBox = (() => {
const trimmedLineString = lineString.trim();
for (const checkboxSet of checkboxSetList) {
for (let i = 0; i < checkboxSet.length; i++) {
if (trimmedLineString.startsWith(checkboxSeti)) { }
}
}
return target.textContent;
})();
writeText(newBox);
toCursorPointer();
}
);
// ボックス行で改行すると次行にボックス自動挿入
$("#text-input").off(keydown.autoInsertCheckBox_${targetProject});
$("#text-input").on(
keydown.autoInsertCheckBox_${targetProject},
async (event) => {
if (scrapbox.Project.name !== targetProject) {
$("#text-input").off(keydown.autoInsertCheckBox_${targetProject});
return;
}
switch (event.key) {
case "Enter": {
let currentLineString;
try {
currentLineString = getCursorLineString();
} catch (err) {
console.log(err);
return;
}
if (!startsWithBoxReg.test(currentLineString)) return;
await new Promise((resolve) => setTimeout(resolve, 30));
let nextLineString;
try {
nextLineString = getCursorLineString();
} catch (err) {
console.log(err);
return;
}
if (!startsWithBoxReg.test(nextLineString)) {
const trimmedLineString = currentLineString.trim();
const targetBoxSet = checkboxSetList.find((boxSet) => {
return boxSet.some((box) => trimmedLineString.startsWith(box));
});
writeText(targetBoxSet0 + " "); toCursorPointer();
}
return;
}
default: {
return;
}
}
}
);
})();