generated at
scrapbox-selection-2
2021-01-08 17:21:40 #text-inputに選択範囲の文字列が入っていた
全ッ然気づかなかった……takker
気づいた背景:
1. Scrapboxの選択範囲を右クリックしてContext Menu Searchを実行すると、選択範囲内の文字列が正しく渡される。
2. takker「これってScrapboxの仕様にかかわらず選択範囲の文字列を取得する方法があるのでは?」
3. document.getElementById('text-input').value が怪しそうなので中身を確認
4. 予想通り入っていた!!!!
なお、選択範囲の開始・終了位置を計算するには、依然このscriptが必要になる
2021-01-10 03:56:34 この結果を元に新しいscriptを作った
hr
Scrapboxの選択範囲の領域と文字列をscriptから取得するAPI

座標だけからscrapboxの選択範囲を取得するのは不可能なため、どうしても正しい選択範囲を取得することができなかった
scrapbox-selection-2では、選択範囲の開始位置を予め記録しておくことで、正確な選択範囲を取得できるようにした

2021-01-01
2020-12-30
08:38:51 選択範囲が一瞬で消えたときにerrorが出てしまう問題を解消した

テストコードtakker
test1.js
import {selection} from '/api/code/takker/scrapbox-selection-2/script.js'; window.selection = selection; const observer = new MutationObserver(mutations => mutations.forEach(mutation => console.log(selection.text))); observer.observe(document.getElementsByClassName('cursor')?.[0], {attributes: true});

dependencies
script.js
import {scrapboxDOM} from '/api/code/takker/scrapbox-dom-accessor/script.js'; import {getCharBorder} from '/api/code/takker/scrapbox-position/script.js'; import {CursorObserver} from '/api/code/takker/scrapbox-cursor-observer/script.js'; import {cursor} from '/api/code/takker/scrapbox-cursor-position-3/script.js'; import {char as c} from '/api/code/takker/scrapbox-char-info/script.js'; import {line as l} from '/api/code/takker/scrapbox-line-info-2/script.js'; // 左右の文字のDOMの間の番号を取得する const borderIndex = ({left,right}) => right ? c(right).index : left ? c(left).index + 1 : undefined; class Selection { constructor() { this._cursorObserver = undefined; // 先に.selectionの監視を開始する this._selectionObserver = new MutationObserver(mutations => { if (mutations.flatMap(mutation => [...mutation.addedNodes]) .some(element =>element.classList.contains('selections'))) { this._selectMode = true; this._recordSelectionEdge(); // 選択範囲の開始位置を記録しておく } if (mutations.flatMap(mutation => [...mutation.removedNodes]) .some(element =>element.classList.contains('selections'))) { this._selectMode = false; } }); this._selectionObserver.observe(scrapboxDOM.editor, { childList: true }); this._cursorObserver = new CursorObserver(); this._cursorObserver.start(); this._recordedEdge = {}; } // 選択範囲が存在するかどうか get exist() { return this._selectMode; } // 選択範囲の開始位置と終了位置を返す get range() { if (!this.exist) return {message: '[range@scrapbox-selection-2] No range found.'}; // 現在のcursorの位置 //console.log({cursorEdge: this._cursorObserver, recordedEdge: this._recordedEdge}); // 順番を判定する if (this._recordedEdge.lineNo > this._cursorObserver.lineNo) { return { start: this._cursorObserver, end: this._recordedEdge, }; } if (this._recordedEdge.lineNo < this._cursorObserver.lineNo) { return { start: this._recordedEdge, end: this._cursorObserver, }; } // 行が同じの場合は列で比較する if (this._recordedEdge.index > this._cursorObserver.index) { return { start: this._cursorObserver, end: this._recordedEdge, }; } if (this._recordedEdge.index < this._cursorObserver.index) { return { start: this._recordedEdge, end: this._cursorObserver, }; } // 完全に位置が一致している場合 return {message: '[range@scrapbox-selection-2] the start position is the same as the end one.'}; } get text() { const {start, end, message} = this.range; if (message) { console.log(message); return []; } const texts = scrapbox.Page.lines.map(line => line.text); /*console.log('[text@scrapbox-selection-2] %o', {start,end}) console.log('[text@scrapbox-selection-2] %o', { start: { left: texts[start.lineNo].split('')[start.index - 1], right: texts[start.lineNo].split('')[start.index], line: texts[start.lineNo], }, end: { left: texts[end.lineNo].split('')[end.index - 1], right: texts[end.lineNo].split('')[end.index], line: texts[end.lineNo], }, });*/ // 選択範囲が一行に収まっているとき if (start.lineNo === end.lineNo) { return [texts[start.lineNo].substring(start.index, end.index)]; } // 選択範囲が複数行にまたがるとき return [ texts[start.lineNo].substring(start.index), ...texts.slice(start.lineNo + 1, 1 + end.lineNo - 1), texts[end.lineNo].substring(0, end.index), ]; } //cursorがない方の選択範囲の端を取得する // この関数を呼び出すタイミングではまだ_cursorObserverが呼び出されていないので、独自にcursorの位置を取得する _recordSelectionEdge() { if (!this.exist) return undefined; const selections = scrapboxDOM.selections?.getElementsByClassName('selection'); // 一瞬で選択範囲が消えるとselectionsがundefindeになる場合がある if (!selections || selections?.length === 0) { this._selectMode = false; return undefined; } // 選択範囲の端の座標 const startRect = selections[0].getBoundingClientRect(); const endRect = (selections[2] ?? selections[0]).getBoundingClientRect(); // 選択範囲の端の文字を取得する const start = getCharBorder({x: startRect.left, y: startRect.top}); const end = getCharBorder({x: endRect.right, y: endRect.bottom}); console.log('[scrapbox-selection-2] the selection edges: %o',{start, end}); // 行数と列数を計算する const startEdge = { index: borderIndex(start), lineNo: l(start.line).index, }; const endEdge = { index: borderIndex(end), lineNo: l(end.line).index, }; // cursorの位置を取得する const temp = cursor(); const cursorEdge = { index: temp.index, lineNo: l(temp.line).index, }; // 選択範囲が一行のときは、選択範囲の端の座標から計算した位置を使う // 1行かつ選択範囲の取得開始直後なら記法が展開されていることが保証されている if (startEdge.lineNo === endEdge.lineNo) { if (cursorEdge.index === startEdge.index && cursorEdge.lineNo === startEdge.lineNo) { this._recordedEdge = endEdge; } else { this._recordedEdge = startEdge; } return; } // 選択範囲が複数行に渡るときは、記録しておいたカーソルの位置を用いる // 記法が隠れている可能性があるので、選択範囲の座標から位置を計算できない this._recordedEdge = { index: this._cursorObserver.index, lineNo: this._cursorObserver.lineNo, }; } } export const selection = new Selection();

#2021-01-10 03:56:53
#2021-01-08 17:34:14
#2021-01-01 13:40:09
#2020-12-30 08:38:42
#2020-12-29 07:00:13