副表記へ自動的に転送するUserScript
改修中
ハイライトする処理を自由に指定できるようにしたい
大体出来上がってはいるが、まだ仕様が固まっていないので保留
いつになったらやるのやら
code:script.js
/**
* ❌から始まるリンクを開いた時に自動的に転送します
* fragment-linkが元になっている
*/
export const runCrossLink = async( highlightFunc = highlightLinkedPage ) => {
let mutationObserver;
/**
* 副表記のリンクがあるページへリダイレクトする
* 複数候補がある場合は該当ページをハイライトするに留める
*/
const highlightPagesOrRedirect = async() => {
mutationObserver?.disconnect();
if (!scrapbox.Page.title || scrapbox.Page.title.indexOf("❌") != 0) {
// ページタイトルがない or ❌から始まるページではない
return;
}
// 開いているページの情報を取得
encodeURIComponent(scrapbox.Project.name)
}/${encodeURIComponent(scrapbox.Page.title)}`);
if (!pageAPIResponse.ok) {
throw new Error(${pageAPIResponse.status} ${pageAPIResponse.statusText});
}
const page = await pageAPIResponse.json();
const linkedLinks = [];
for (const { title } of page.relatedPages.links1hop) {
// Links欄(1-hop)にあるページを取得
encodeURIComponent(scrapbox.Project.name)
}/${encodeURIComponent(title)}`);
if (!relatedPageAPIResponse.ok) {
throw new Error(${relatedPageAPIResponse.status} ${relatedPageAPIResponse.statusText});
}
const relatedPage = await relatedPageAPIResponse.json();
// 開いているページへのリンク(つまり逆リンク)を検索
relatedPage.lines.forEach(line => {
if (line.text.indexOf([${scrapbox.Page.title}]) >= 0) {
linkedLinks.push({ line, page: relatedPage})
}
});
}
if (!isPageExists(scrapbox.Page.title) && linkedLinks.length === 1) {
// ページ遷移
const link = document.createElement("a")
link.href = /${scrapbox.Project.name}/${encodeURIComponent(linkedLinks[0].page.title)}#${linkedLinks[0].line.id}
document.body.appendChild(link)
link.click()
}
highlightFunc(linkedLinks);
mutationObserver = new MutationObserver(e=>highlightFunc(linkedLinks));
mutationObserver.observe(document.body, {
subtree: true,
childList: true,
characterData: true,
});
};
await highlightPagesOrRedirect();
scrapbox.on('page:changed', highlightPagesOrRedirect);
};
/**
* ページが存在するか(既に作成されているか)を確かめる
* @param {string} pageTitle ページのタイトル
* @returns {boolean} 存在すればtrue, 存在しなければfalse
*/
function isPageExists(pageTitle) {
const pages = scrapbox.Project.pages
for(let page of pages) {
if(page.title == pageTitle) {
return page.exists
}
}
return false
}
/**
* ページカードをハイライトする関数
* ハイライトの処理を書くときの参考にしてください。
* (この関数の仕様は後で変えるかも)
*/
function highlightLinkedPage(linkedLinks) {
const relatedPageListItemElements = [
...document.querySelectorAll(".related-page-list .grid li")
];
// linkedLinksの中には予め対象のページリンクの情報が入っている
linkedLinks.forEach((linkedLink) =>
relatedPageListItemElements.forEach((relatedPageListItemElement) => {
if (relatedPageListItemElement.dataset.pageTitle !== linkedLink.page.title) {
return;
}
// ページカードをハイライトする
relatedPageListItemElement.querySelector("a .header").style.borderColor
})
);
}
改修前
code:script_old.js
export const runCrossLink = async() => {
let mutationObserver;
/**
* 副表記のリンクがあるページへリダイレクトする
* 複数候補がある場合は該当ページをハイライトするに留める
*/
const highlightPagesOrRedirect = async() => {
mutationObserver?.disconnect();
if (!scrapbox.Page.title || scrapbox.Page.title.indexOf("❌") != 0) {
// ページタイトルがない or ❌から始まるページではない
return;
}
// 開いているページの情報を取得
encodeURIComponent(scrapbox.Project.name)
}/${encodeURIComponent(scrapbox.Page.title)}`);
if (!pageAPIResponse.ok) {
throw new Error(${pageAPIResponse.status} ${pageAPIResponse.statusText});
}
const page = await pageAPIResponse.json();
const linkedLinks = [];
for (const { title } of page.relatedPages.links1hop) {
// Links欄(1-hop)にあるページを取得
encodeURIComponent(scrapbox.Project.name)
}/${encodeURIComponent(title)}`);
if (!relatedPageAPIResponse.ok) {
throw new Error(${relatedPageAPIResponse.status} ${relatedPageAPIResponse.statusText});
}
const relatedPage = await relatedPageAPIResponse.json();
// 開いているページへのリンク(つまり逆リンク)を検索
relatedPage.lines.forEach(line => {
if (line.text.indexOf([${scrapbox.Page.title}]) >= 0 ||
line.text.indexOf(#${scrapbox.Page.title}) >= 0) {
// ハッシュタグに対応させるのを忘れたので、突貫工事で対応させたが、様々なバグが発生している可能性がある
// 例えば、Aというページの逆リンクを探しているのに、#ABというハッシュタグがヒットする可能性など
linkedLinks.push({ line, page: relatedPage})
}
});
}
if (!isPageExists(scrapbox.Page.title) && linkedLinks.length === 1) {
// ページ遷移
const link = document.createElement("a")
link.href = /${scrapbox.Project.name}/${encodeURIComponent(linkedLinks[0].page.title)}#${linkedLinks[0].line.id}
document.body.appendChild(link)
link.click()
}
const highlightLinkedPages = () => {
const relatedPageListItemElements = [
...document.querySelectorAll(".related-page-list .grid li")
];
linkedLinks.forEach((linkedLink) =>
relatedPageListItemElements.forEach((relatedPageListItemElement) => {
if (relatedPageListItemElement.dataset.pageTitle !== linkedLink.page.title) {
return;
}
relatedPageListItemElement.querySelector("a .header").style.borderColor
})
);
};
highlightLinkedPages();
mutationObserver = new MutationObserver(highlightLinkedPages);
mutationObserver.observe(document.body, {
subtree: true,
childList: true,
characterData: true,
});
};
await highlightPagesOrRedirect();
scrapbox.on('page:changed', highlightPagesOrRedirect);
};
/**
* ページが存在するか(既に作成されているか)を確かめる
* @param pageTitle ページのタイトル
* @returns 存在すればtrue, 存在しなければfalse
*/
function isPageExists(pageTitle) {
const pages = scrapbox.Project.pages
for(let page of pages) {
if(page.title == pageTitle) {
return page.exists
}
}
return false
}