omakase-link-script
code:script.js
const loadTitles = ({useLinks}) => {
fetch(/api/pages/${scrapbox.Project.name}/search/titles, {
credentials: 'same-origin'
}).then(res => {
return res.json()
}).then(data => {
let titles = data.map(p => p.title)
if (useLinks) {
data.map(p => {
p.links.map(l => {if (!titles.includes(l)) titles.push(l)})
})
}
titles = titles.sort((a, b) => b.length - a.length)
window.titles = titles
console.log('omakase-links-✨: load titles, links')
scrapbox.PopupMenu.addButton({
title: '✨',
onClick: autoLink
})
})
}
// 既に記法になっているなどの理由で、置換すべきでない範囲を取得する
const detectLocked = text => {
const syntax = /\[*\.+?\\]*/g const locked = text.split('').map(c => false)
let res
while (res = syntax.exec(text)) {
}
return locked
}
const autoLink = text => {
if (!window.titles || !text) return text
const locked = detectLocked(text)
const matched = text.split('').map(c => null)
for (const title of window.titles) {
if (title === document.getElementsByClassName('lines')0.firstElementChild.textContent || title === window.location.href.split("/")4.replace(/_/g," ")) continue; const regexp = new RegExp(escapeTitle(title), 'gi')
let res, found = false
while (!found && (res = regexp.exec(text))) {
const idx = res.index
if (matchedidx === null && !lockedidx) { found = true
for (let i = 0; i < len; i++) {
if (i === 0 || i === len - 1) {
if (i === 0) c = [${c}
if (i === len - 1) c = ${c}]
} else {
}
}
}
}
}
// 無駄にテロメアがupdateされるのを防ぐ為、何も置換しない時は何も返さない
if (matched.join('').length === 0) return
// 配列matchedでnullの位置を、元のtextの文字に置き換える
return matched.map((c, idx) => c === null ? textidx : c).join('') }
const escapeTitle = title => {
return title.replace(/[$-\/?-^{|}/g, '\\$&') }
// 置換候補をリアルタイムで更新できないのがツライところ
// しかし、毎回API呼んでると時間かかるので、ひとまず妥協
if (!window.titles) loadTitles({useLinks: true})