scrapbox-cursor-jumper
新しいの作った
提供する関数
jumpToChar
指定した文字にcursorを移動させる
jumpToLF
指定した行の末尾にcursorを移動させる
2つともwindowのscrollを伴う
code
指定した文字にcursorを移動させる
リンク、code blockの先頭、画像、動画、tableがあるとjumpできない
script.js/**
* @param {string} id - 移動したい行のid
* @param {number} index - 移動したい文字の番号 (span.c-{n}のnと同じ)
* XXKeyは同時に押しているとみなしたいmetaKeyを入れる
* @return {void}
*/
export function jumpToChar({id, index, margin = 0, shiftKey = false, ctrlKey = false, altKey = false} = {}) {
const char = document.getElementById(id).getElementsByClassName(`c-${index}`)[0];
scrollToElement({element: char, margin: margin});
const {left, top, height} = char.getBoundingClientRect();
const mouseOptions = {
button: 0,
clientX: left,
clientY: top + height / 2,
bubbles: true,
cancelable: true,
shiftKey: shiftKey,
ctrlKey: ctrlKey,
altKey: altKey,
view: window
};
// cursorを移動する
char.dispatchEvent(new MouseEvent("mousedown", mouseOptions));
char.dispatchEvent(new MouseEvent("mouseup", mouseOptions));
}
指定した行の末尾にcursorを移動させる
任意の行で実行できる
動画、tableでは未確認 (多分動く)
2020-11-28 19:43:04 折返し行にも対応できるようになったはず
script.js/**
* @param {string} id - 移動したい行のid
* XXKeyは同時に押しているとみなしたいmetaKeyを入れる
* @return {void}
*/
export function jumpToLF({id, margin = 0, shiftKey = false, ctrlKey = false, altKey = false} = {}) {
const line = document.getElementById(id).getElementsByClassName('text')[0];
//console.log('Go to %o', line);
scrollToElement({element: line, margin: margin});
const {right, top, height} = line.getBoundingClientRect();
const breakNum = getLineBreakNum({line: line});
//console.log(`right = ${right}, top = ${top}, height = ${height}, breakNum = ${breakNum}`);
const mouseOptions = {
button: 0,
clientX: right,
clientY: top + height - height / (2 * breakNum),
bubbles: true,
cancelable: true,
shiftKey: shiftKey,
ctrlKey: ctrlKey,
altKey: altKey,
view: window
};
line.dispatchEvent(new MouseEvent("mousedown", mouseOptions));
line.dispatchEvent(new MouseEvent("mouseup", mouseOptions));
}
Utilites
指定した要素が画面内に表示されるように上下にscrollする
状況によってscroll方法を変える
既に画面内にある
何もしない
画面の下に隠れている
要素が画面の丁度下に見えるようにscrollする
画面の上に隠れている
要素が画面の丁度上に見えるようにする
option
margin
要素と画面枠との間隔
少し隙間を開けたいときにpixel単位で指定する
defaultは 0
画面内に要素があるときは無視する
script.jsfunction scrollToElement({element, margin = 0} = {}) {
const {top, bottom} = element.getBoundingClientRect();
// 画面内なら何もしない
if (top >= margin && bottom + margin <= window.innerHeight) return;
// 画面の上に隠れているとき
if (top < margin) {
window.scrollBy(0, top - margin);
return;
}
// 画面の下に隠れているとき
if (bottom + margin > window.innerHeight) {
window.scrollBy(0, bottom + margin - window.innerHeight);
return;
}
}
画面上に描画されている実際の行数を取得する
何回折り返されているかを調べる
line: HTMLSpanElement
script.jsfunction getLineBreakNum({line}) {
if (!line) return undefined;
const lineHeight = parseInt(getComputedStyle(line).lineHeight);
const breakNum = Math.floor(line.getBoundingClientRect().height / lineHeight);
return breakNum < 1 ? 1: breakNum;
}
テストコード
test.jsimport {installCDN} from '/api/code/villagepump/install-CDN/script.js'
import {jumpToLF} from '/api/code/takker/scrapbox-cursor-jumper/script.js';
(async () => {
await installCDN({id: 'mousetrap-for-scrapbox', src: '//cdnjs.cloudflare.com/ajax/libs/mousetrap/1.6.5/mousetrap.min.js'});
const editor = document.getElementById('editor');
const moustrapOnEdit = new Mousetrap(editor);
const lines = document.getElementsByClassName('lines')[0];
for (let i = 0; i < 10; i++) {
moustrapOnEdit.bind(`alt+g ctrl+${i}`, e =>{
_log(`${e.key} is pressed.`);
e.stopPropagation();
e.preventDefault();
jumpToLF({id: getLineId(i), margin: 50})
});
moustrapOnEdit.bind(`alt+x ctrl+${i}`, e =>{
_log(`${e.key} is pressed.`);
e.stopPropagation();
e.preventDefault();
jumpToLF({id: getLineId(lines.children.length - 1 - i), margin: 50})
});
}
function _log(msg, ...objects){
if (objects.length > 0) {
console.log(`[scrapbox-vim-bindings] ${msg}`, objects);
return;
}
console.log(`[scrapbox-vim-bindings] ${msg}`);
}
function getLineId(number) {
return lines.children[number].id;
}
})();
以下、試行錯誤
script_old.jsexport function jumpCursor({id, index, shiftKey = false, ctrlKey = false, altKey = false} = {}) {
const char = document.getElementById(id).getElementsByClassName(`c-${index}`)[0];
console.log(char);
const {left, top,width,height} = char.getBoundingClientRect();
const mouseOptions = {
button: 0,
clientX: left,
clientY: top,
bubbles: true,
cancelable: true,
shiftKey: shiftKey,
ctrlKey: ctrlKey,
altKey: altKey,
view: window
};
char.dispatchEvent(new MouseEvent("mousedown", mouseOptions));
char.dispatchEvent(new MouseEvent("mouseup", mouseOptions));
}
既知の問題
code blockの先頭には飛べない
リンクがあると正常に飛べない
リンクやcode blockの影響を受けないように、末尾にcursorを置いてみる
これでもうまく行かない
script_old.jsexport function jumpCursor2({id, shiftKey = false, ctrlKey = false, altKey = false} = {}) {
const char = [...document.getElementById(id).querySelectorAll('span[class^="c-"]')].pop();
console.log(char);
const {right, top, height} = char.getBoundingClientRect();
const mouseOptions = {
button: 0,
clientX: right + 5, //すこし後ろにずらす
clientY: top + height/2,
bubbles: true,
cancelable: true,
shiftKey: shiftKey,
ctrlKey: ctrlKey,
altKey: altKey,
view: window
};
char.dispatchEvent(new MouseEvent("mousedown", mouseOptions));
char.dispatchEvent(new MouseEvent("mouseup", mouseOptions));
}
lineを直接押せないかな?
2020-11-24 12:24:17 押せた!
script_old.jsexport function jumpLF({id, shiftKey = false, ctrlKey = false, altKey = false} = {}) {
const line = document.getElementById(id).getElementsByClassName('text')[0];
console.log(line);
const {right, top, height} = line.getBoundingClientRect();
const mouseOptions = {
button: 0,
clientX: right,
clientY: top + height/2,
bubbles: true,
cancelable: true,
shiftKey: shiftKey,
ctrlKey: ctrlKey,
altKey: altKey,
view: window
};
line.dispatchEvent(new MouseEvent("mousedown", mouseOptions));
line.dispatchEvent(new MouseEvent("mouseup", mouseOptions));
}
テストコード
test_old.jsimport {installCDN} from '/api/code/villagepump/install-CDN/script.js'
import {jumpCursor, jumpCursor2, jumpLF} from '/api/code/takker/scrapbox-cursor-jumper/script_old.js';
test();
async function test() {
await installCDN({id: 'mousetrap-for-scrapbox', src: '//cdnjs.cloudflare.com/ajax/libs/mousetrap/1.6.5/mousetrap.min.js'});
const editor = document.getElementById('editor');
const moustrapOnEdit = new Mousetrap(editor);
const lines = document.getElementsByClassName('lines')[0];
for (let i = 0; i < 10; i++) {
moustrapOnEdit.bind(`alt+g ctrl+${i}`, e =>{
_log(`${e.key} is pressed.`);
e.stopPropagation();
e.preventDefault();
window.scroll(0,0);
jumpLF({id: getLineId(i)})
});
moustrapOnEdit.bind(`alt+x ctrl+${i}`, e =>{
_log(`${e.key} is pressed.`);
e.stopPropagation();
e.preventDefault();
const tailLine = lines.lastElementChild;
const {bottom} = tailLine.getBoundingClientRect();
window.scrollBy(0,bottom - window.innerHeight);
jumpLF({id: getLineId(lines.children.length - 1 - i)})
});
}
function _log(msg, ...objects){
if (objects.length > 0) {
console.log(`[scrapbox-vim-bindings] ${msg}`, objects);
return;
}
console.log(`[scrapbox-vim-bindings] ${msg}`);
}
function getLineId(number) {
return lines.children[number].id;
}
function getLastLineId() {
return lines.lastElementChild.id;
}
}