generated at
scrapbox-cursor-jumper
新しいの作った
hr

提供する関数
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
画面内に要素があるときは無視する
Element.scrollIntoView()でも似たようなことはできる
script.js
function 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.js
function 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.js
import {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; } })();

以下、試行錯誤
hr
script_old.js
export 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.js
export 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.js
export 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)); }

テストコードtakker
test_old.js
import {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; } }

#2020-11-28 19:55:03
#2020-11-24 00:26:17