scrapbox-userscript-for-ipad
外部キーボード利用時に以下の
command + i: 入力モード切り替え
command + c: コピー
command + x: カット
command + v: ペースト
code:script.ts
import {
indentLines, deindentLines, upLines, downLines,
indentBlocks, deindentBlocks, upBlocks, downBlocks,
focusEnd, insertText, caret, takeSelection, takeCursor, press
} from "../scrapbox-userscript-std/mod.ts";
const selection = takeSelection();
const cursor = takeCursor();
const sleep = (milliseconds) => new Promise(resolve => setTimeout(resolve, milliseconds));
function createTouchEvent(element, type, X, Y) {
console.log(createTouchEvent: ${type});
const touch = new Touch({
identifier: 0,
target: element,
clientX: X,
clientY: Y,
pageX: X + window.scrollX,
pageY: Y + window.scrollY,
});
const touchList = document.createTouchList(touch);
const param = {
type: type,
canBubble: true,
cancelable: true,
view: window,
detail: 0,
screenX: X + window.scrollX,
screenY: Y + window.scrollY,
clientX: X,
clientY: Y,
ctrlKey: false,
altKey: false,
shiftKey: false,
metaKey: false,
touches: touchList,
targetTouches: touchList,
changedTouches: touchList,
scale: 0,
rotation: 0,
touchItem: 0
}
let event = document.createEvent('TouchEvent');
event.initTouchEvent(
param.type,
param.canBubble,
param.cancelable,
param.view,
param.detail,
param.screenX,
param.screenY,
param.clientX,
param.clientY,
param.ctrlKey,
param.altKey,
param.shiftKey,
param.metaKey,
param.touches,
param.targetTouches,
param.changedTouches,
param.scale,
param.rotation);
return event;
}
async function mimicHoldDown(element, { X, Y, holding = 2000, wait = 10 } = {}) {
element.dispatchEvent(createTouchEvent(element, 'touchstart', X, Y));
await sleep(holding);
element.dispatchEvent(createTouchEvent(element, 'touchend', X, Y));
await sleep(wait); // 少し待つ
}
function getHeadLineDOM() {
return document.getElementById(L${scrapbox.Page.lines[0].id});
}
function position({ pos = 'right' } = {}) {
const { top, left } = document.getElementById('editor').getBoundingClientRect(); // 基準座標
const style = document.getElementsByClassName('cursor')?.0.style; const cursor = {
top: parseInt(style.top),
left: parseInt(style.left),
height: parseInt(style.height),
};
const position = {
x: cursor.left + left + 1,
y: (cursor.top + top) + cursor.height / 2,
};
const targets = document.elementsFromPoint(position.x, position.y);
const char = targets.find(target => target.classList.contains('char-index'));
const line = targets.find(target => target.classList.contains('line'));
return { char, line };
};
function isHeightViewable(element) {
const { top, bottom } = element.getBoundingClientRect();
return top >= 0 && bottom <= window.innerHeight;
}
// iPad Safari(デスクトップ用Webページ)
if (/Macintosh/.test(navigator.userAgent) && 'ontouchend' in document) {
$(document).keydown(async (e) => {
switch (e.key) {
case 'i':
if (e.metaKey) {
console.log('input mode');
const target = (position().line ?? getHeadLineDOM())?.getElementsByClassName('text')?.0; const { right, top, height } = target.getBoundingClientRect();
await mimicHoldDown(target, { X: right + 1, Y: top + height / 2 });
}
break;
default:
break;
return false;
}
});
$("#text-input").keydown(async (e) => {
console.log('Copy');
switch (e.key) {
case 'c':
if (e.metaKey) {
console.log('copy');
try {
const text = caret().selectedText;
if (!text) return;
await navigator.clipboard.writeText(text);
} catch (e: unknown) {
console.error(e);
alert(Faild to copy:\n${JSON.stringify(e)});
}
}
break;
case 'x':
if (e.metaKey) {
console.log('切り取り');
try {
const hasSelection = selection.hasSelection();
const start = selection.getRange().start.line;
const text = hasSelection
? selection.getSelectedText()
: getText(start);
if (!text) return;
await navigator.clipboard.writeText(text);
if (!hasSelection) {
selection.setRange({
start: { line: start, char: 0 },
end: { line: start, char: text.length },
});
}
press("Delete");
} catch (e: unknown) {
console.error(e);
alert(Faild to cut:\n${JSON.stringify(e)});
}
}
break;
case 'v':
if (e.metaKey) {
console.log('ペースト');
try {
const text = await navigator.clipboard.readText();
if (!text) return;
cursor.focus();
await insertText(text);
} catch (e: unknown) {
console.error(e);
alert(Faild to paste:\n${JSON.stringify(e)});
}
}
break;
default:
console.log(e);
}
return false;
});
}// end /iPad/i.test(navigator.userAgent)