generated at
pin-diary-5
pin-diaryの実装その5
pin-diary-4 -> pin-diary-5 -> pin-diary-6

使い方
お試し
await import("../../villagepump/pin-diary-5/script.js"); を自分のページの script.js に貼り付ける
ちゃんと使う
いくつか方法があります
このリンクから取得したコードを自分のページに貼り付ける
コードを更新したいときは &reload を↑のURLの後ろに付けてください
deno bundle https://scrapbox.io/api/code/villagepump/pin-diary-5/script.js
カスタマイズ
↓のように、 launch() の引数にproject nameを入れることで、pin-diaryを使いたいprojectを変更できます
script.js
import { launch } from "./mod.js"; launch("villagepump");
特定日のページをPin留めページにしたいとき
js
import { pinDiary } from "./mod.js"; await pinDiary("villagepump", new Date(2144, 0, 10));

特徴
ページの有無に関わらず、任意のプロジェクトの任意のページからその日の日記をピン留めできる
このGIFだとピン留めしている様子がわからないtakker
まあ別にいいか
右下の通信ステータスを見るしかないっぽいMijinko_SD
と強調してあるが、実際は/villagepumpにいないと発動しないよう制限がかかっている
APIを叩きすぎないようにするため
日記ページのtemplateが入っていなかったら勝手に書き込む
差し込み処理が結構雑
作成状況を右下の通信ステータスに表示する

実装
ScrapboxのWebSocketを模倣することで、backgroundから全ての操作を行っている
pin-diary-4はピン留め操作のみをbackgroundからやっていた
今回は全ての編集操作にwebsocketを採用した
外部自製ライブラリをバリバリ使っている
bundleサイズ増えちゃったかも……
scrapbox-parserがまるごと入っている
コードの追跡が面倒になるというデメリットもある……
外部ライブラリは deps.ts にまとまっています
テストコードに使った外部ライブラリは deps_test.ts に置いた

改善したいところ
donepin-diary-4 listDiaries() の実装に無駄が有りすぎる
APIを叩きすぎ
もっと軽く実装し直したい
09:17:44 実装した

バグ
done23:00:04 今スマホでscrapbox開いたら、 [2] という謎ページが生成&ピン留めされた
なんだこれ
どっからでてきたこいつ
明日調べますtakker
2022-02-12
07:59:17 日記ページのtemplateの返り値の配列を分解したときにおかしなデータができている
[title, header, footer] === [2, 0, 2]
getTemplate() 自体は正常な値を返している
ts
import { getTemplate } from "../日記ページのtemplate/diary.ts"; console.log(getTemplate(new Date()));
08:03:56 minifyした getTemplate() が配列ではなく文字列を返していた
.join("\n") しちゃっている
08:05:54 解決
単に自分がbundleしたコードの getTemplate() のversionが古かっただけでした
scrapbox-bundlercacheを消していなかった
お騒がせしました……
doneピンを外す処理で与えられた日付を使っていない
書き換え忘れていた
2022-02-12 08:52:22 直した
done今日の日付以外の日記ページのPinがはずれない
!== === を間違えていた

不必要にややこしくなってしまったtakker
algorithmは完全に効率度外視で書いた
流石にコードに自信が持てなかったのでテストを書いた


mod.ts
/// <reference no-default-lib="true"/> /// <reference lib="esnext"/> /// <reference lib="dom"/> import { patchTemplate } from "./format.ts"; import { pin, unpin, patch, useStatusBar, sleep, getTemplate, } from "./deps.ts"; import { listPinnedDiaries } from "./list.ts"; import type { Scrapbox } from "./deps.ts"; declare const scrapbox: Scrapbox; // initialize export function launch(project: string, interval = 24 * 3600 * 1000) { const handleChange = () => scrapbox.Project.name === project ? startObserve(project, interval) : endObserve(); handleChange(); scrapbox.addListener("project:changed", handleChange); }

一定時間ごとにピン留めを更新する
mod.ts
let updateTimer: number | undefined; async function startObserve(project: string, interval: number) { endObserve(); await pinDiary(project, new Date()); updateTimer = setInterval( () => pinDiary(project, new Date()), interval, ); } function endObserve() { clearInterval(updateTimer); }

project 中の date で指定した日付ページをピン留めする
mod.ts
export async function pinDiary(project: string, date: Date): Promise<void> { const { render, dispose } = useStatusBar(); try { // 今日以外の日付ページを外す render( { type: "spinner" }, { type: "text", text: `unpin other diary pages...`}, ); for await (const { title } of listPinnedDiaries(project)) { if (title === toYYYYMMDD(date)) continue; await unpin(project, title); } // 今日の日付ページをピン留めする const [title, header, footer] = getTemplate(date); render( { type: "spinner" }, { type: "text", text: `pin "/${project}/${title}"...`}, ); await pin(project, title, { create: true }); // 今日の日付ページにtemplateを挿入する const headers = header.split("\n"); const footers = footer.split("\n"); render( { type: "spinner" }, { type: "text", text: `format "/${project}/${title}"...`}, ); await patch(project, title, (lines) => [ lines[0].text, ...patchTemplate( lines.slice(1).map(line => line.text), headers, footers, ), ]); render( { type: "check-circle" }, { type: "text", text: `Pinned "/${project}/${title}".`}, ); } catch(e: unknown) { render( { type: "exclamation-triangle" }, { type: "text", text: e instanceof Error ? `${e.name} ${e.message}` : `Unknown error! (see developper console)`, }, ); console.error(e); } finally { await sleep(1000); dispose(); } } function toYYYYMMDD(date: Date) { return `${date.getFullYear()}/${zero(date.getMonth() + 1)}/${ zero(date.getDate()) }`; } function zero(n: number) { return String(n).padStart(2, "0"); }

ピン留めされた日記ページを探す
list.ts
import { listPages, PageSummary } from "./deps.ts"; export async function* listPinnedDiaries(project: string): AsyncGenerator<PageSummary> { for await (const page of listPinnedPages(project)) { if (!/\d{4}\/\d{2}\/\d{2}/.test(page.title)) continue; yield page; } } /** 全てのピン留めされたページを取得する */ async function* listPinnedPages(project: string, skip = 0): AsyncGenerator<PageSummary> { const { count, pages } = await ensureList(project, skip); for (const page of pages) { if (page.pin === 0) continue; yield page; } if ((pages.at(-1)?.pin ?? 0) === 0) return; yield* listPinnedPages(project, skip + 1000); } async function ensureList(project: string, skip: number) { const result = await listPages(project, { limit: 1000, skip }); // login errorなどは全部例外として扱う if (!result.ok) { const error = new Error(); error.name = result.value.name; error.message = result.value.message; throw error; } return result.value; }

dependencies
型定義ファイル
deps.ts
export { getTemplate } from "../日記ページのtemplate/diary.ts"; export { pin, unpin, patch, useStatusBar, listPages, } from "https://raw.githubusercontent.com/takker99/scrapbox-userscript-std/0.24.1/mod.ts"; export { sleep } from "https://raw.githubusercontent.com/takker99/scrapbox-userscript-std/0.24.1/sleep.ts"; export type { Scrapbox, } from "https://raw.githubusercontent.com/scrapbox-jp/types/0.5.0/userscript.ts"; export type { PageSummary, } from "https://raw.githubusercontent.com/scrapbox-jp/types/0.5.0/rest.ts";
deps_test.ts
export { assertEquals } from "https://deno.land/std@0.125.0/testing/asserts.ts";

日記ページにtemplateを入れる
バグ
format.ts
import { patchLines, findSplitIndex } from "./util.ts"; // linesにタイトルを入れないように export const patchTemplate = (lines: string[], headers: string[], footers: string[]): string[] => { // headerとfooterに相当する行を補う const bodies = patchLines( patchLines(lines, headers).reverse(),

Array.prototype.reverse()は元の配列を変更する
Array.prototype.splice()パターンかよ止めてくれ……takker
format.ts
[...footers].reverse(), ).reverse(); // headerとfooterの間に余裕をもたせる const headerStart = findSplitIndex(bodies, headers); const footerStart = bodies.length - 1 - findSplitIndex( [...bodies].reverse(), [...footers].reverse(), ); return [ ...bodies.slice(0, headerStart + 1), "", ...bodies.slice(headerStart + 1, footerStart).join("\n").trim().split("\n"), "", ...bodies.slice(footerStart), ]; }

$ deno test https://scrapbox.io/api/code/villagepump/pin-diary-5/format_test.ts
format_test.ts
import { patchTemplate } from "./format.ts"; import { assertEquals } from "./deps_test.ts"; Deno.test("patchTemplate()", () => { const headers = [ "header start", "header content", "", "header end", ]; const lines = [ "何か", "aaa", "header content", " コメントが書いてあるかも", "header end modified", "", "本文とか", "おしまい", "footer end", " ↑footerの残骸", ]; const footers = [ "footer start", "footer content", "footer end", ]; assertEquals<string[]>(patchTemplate(lines, headers, footers), [ "header start", "何か", "aaa", "header content", " コメントが書いてあるかも", "header end modified", "", "header end", "", "本文とか", "おしまい", "", "footer start", "footer content", "footer end", " ↑footerの残骸", ]); });

ココもアロー化できるSummer498Done
util.ts
export const patchLines = (lines: string[], appends: string[]): string[] => { let index = 0; const result = [] as string[]; for (let i = 0; i < appends.length; i++) { const pos = lines.findIndex((line, j) => j >= index && line.trim() === appends[i].trim()); if (pos < 0) { result.push(appends[i]); continue; } result.push(...lines.slice(index, pos + 1)); index = pos + 1; } result.push(...lines.slice(index)); return result; } export const findSplitIndex = (lines: string[], query: string[]) => { let index = -1; for (const text of query) { const pos = lines.findIndex((line, j) => j > index && line.trim() === text.trim()); if (pos < 0) return -1; index = pos; } return index; }

$ deno test https://scrapbox.io/api/code/villagepump/pin-diary-5/util_test.ts
util_test.ts
import { patchLines } from "./util.ts"; import { assertEquals } from "./deps_test.ts"; Deno.test("patchLines()", () => { const template = [ "template start", "template template", "template end", ]; const lines = [ "何か", "aaa", "template template", " コメントが書いてあるかも", "template end modified", "", "本文とか", "おしまい", ]; assertEquals<string[]>(patchLines(lines, template), [ "template start", "何か", "aaa", "template template", "template end", " コメントが書いてあるかも", "template end modified", "", "本文とか", "おしまい", ]); })


hr
bundled code
mod.js
function G(e,t){let n=0,r=[];for(let o=0;o<t.length;o++){let s=e.findIndex((i,a)=>a>=n&&i.trim()===t[o].trim());if(s<0){r.push(t[o]);continue}r.push(...e.slice(n,s+1)),n=s+1}return r.push(...e.slice(n)),r}function X(e,t){let n=-1;for(let r of t){let o=e.findIndex((s,i)=>i>n&&s.trim()===r.trim());if(o<0)return-1;n=o}return n}function be(e,t,n){let r=G(G(e,t).reverse(),[...n].reverse()).reverse(),o=X(r,t),s=r.length-1-X([...r].reverse(),[...n].reverse());return[...r.slice(0,o+1),"",...r.slice(o+1,s).join(` `).trim().split(` `),"",...r.slice(s)]}function u(e,t){if(t.length<e)throw new TypeError(e+" argument"+(e>1?"s":"")+" required, but only "+t.length+" present")}function l(e){u(1,arguments);let t=Object.prototype.toString.call(e);return e instanceof Date||typeof e=="object"&&t==="[object Date]"?new Date(e.getTime()):typeof e=="number"||t==="[object Number]"?new Date(e):((typeof e=="string"||t==="[object String]")&&typeof console!="undefined"&&(console.warn("Starting with v2.0.0-beta.1 date-fns doesn't accept strings as date arguments. Please use `parseISO` to parse strings. See: https://git.io/fjule"),console.warn(new Error().stack)),new Date(NaN))}function V(e){u(1,arguments);let n=l(e).getFullYear();return n%400==0||n%4==0&&n%100!=0}function J(e){u(1,arguments);let t=l(e);return String(new Date(t))==="Invalid Date"?NaN:V(t)?366:365}function Z(e){u(1,arguments);let t=l(e),n=new Date(0);return n.setFullYear(t.getFullYear(),0,1),n.setHours(0,0,0,0),n}function A(e){let t=new Date(Date.UTC(e.getFullYear(),e.getMonth(),e.getDate(),e.getHours(),e.getMinutes(),e.getSeconds(),e.getMilliseconds()));return t.setUTCFullYear(e.getFullYear()),e.getTime()-t.getTime()}function Y(e){u(1,arguments);let t=l(e);return t.setHours(0,0,0,0),t}var Xe=864e5;function ee(e,t){u(2,arguments);let n=Y(e),r=Y(t),o=n.getTime()-A(n),s=r.getTime()-A(r);return Math.round((o-s)/Xe)}function te(e){u(1,arguments);let t=l(e);return ee(t,Z(t))+1}function f(e){if(e===null||e===!0||e===!1)return NaN;let t=Number(e);return isNaN(t)?t:t<0?Math.ceil(t):Math.floor(t)}function I(e,t){u(2,arguments);let n=l(e),r=f(t);return isNaN(r)?new Date(NaN):(r&&n.setDate(n.getDate()+r),n)}function K(e,t){u(2,arguments);let n=f(t);return I(e,-n)}function ne(e,t){u(2,arguments);let r=f(t)*7;return I(e,r)}function $(e,t){u(2,arguments);let n=f(t);return ne(e,-n)}function _(e,t){u(2,arguments);let n=l(e),r=f(t);if(isNaN(r))return new Date(NaN);if(!r)return n;let o=n.getDate(),s=new Date(n.getTime());s.setMonth(n.getMonth()+r+1,0);let i=s.getDate();return o>=i?s:(n.setFullYear(s.getFullYear(),s.getMonth(),o),n)}function R(e,t){u(2,arguments);let n=f(t);return _(e,-n)}function re(e,t){u(2,arguments);let n=f(t);return _(e,n*12)}function oe(e,t){u(2,arguments);let n=f(t);return re(e,-n)}function se(e){return u(1,arguments),l(e).getDay()}function ie(e){return u(1,arguments),l(e).getFullYear()}function T(e,t){for(var n=e<0?"-":"",r=Math.abs(e).toString();r.length<t;)r="0"+r;return n+r}var Ve={y(e,t){let n=e.getUTCFullYear(),r=n>0?n:1-n;return T(t==="yy"?r%100:r,t.length)},M(e,t){let n=e.getUTCMonth();return t==="M"?String(n+1):T(n+1,2)},d(e,t){return T(e.getUTCDate(),t.length)},a(e,t){let n=e.getUTCHours()/12>=1?"pm":"am";switch(t){case"a":case"aa":return n.toUpperCase();case"aaa":return n;case"aaaaa":return n[0];case"aaaa":default:return n==="am"?"a.m.":"p.m."}},h(e,t){return T(e.getUTCHours()%12||12,t.length)},H(e,t){return T(e.getUTCHours(),t.length)},m(e,t){return T(e.getUTCMinutes(),t.length)},s(e,t){return T(e.getUTCSeconds(),t.length)},S(e,t){let n=t.length,r=e.getUTCMilliseconds(),o=Math.floor(r*Math.pow(10,n-3));return T(o,t.length)}},we=Ve;function ae(e){u(1,arguments);var t=l(e);return!isNaN(t)}function ce(e,t){u(2,arguments);let n=l(e).getTime(),r=f(t);return new Date(n+r)}function ue(e,t){u(2,arguments);let n=f(t);return ce(e,-n)}var Je=/(\w)\1*|''|'(''|[^'])+('|$)|./g,Ze=/^'([^]*?)'?$/,et=/''/g,tt=/[a-zA-Z]/;function g(e,t){u(2,arguments);let n=l(e);if(!ae(n))throw new RangeError("Invalid time value");let r=A(n),o=ue(n,r),s=t.match(Je);return s?s.map(a=>{if(a==="''")return"'";let c=a[0];if(c==="'")return nt(a);let p=we[c];if(p)return p(o,a);if(c.match(tt))throw new RangeError("Format string contains an unescaped latin alphabet character `"+c+"`");return a}).join(""):""}function nt(e){let t=e.match(Ze);return t?t[1].replace(et,"'"):e}var b="yyyy/MM/dd",de=e=>[g(e,b),[`[${g(K(e,1),b)}.icon]←${g(e,b)}→[${g(I(e,1),b)}.icon]`,`[[${rt(e)}曜日]]`,`${ie(e)}年 ${(te(e)*100/J(e)).toFixed(2)}%経過`,`[🌏 https://ja.wikipedia.org/wiki/${g(e,"M月d日")}]`,[`[${g($(e,1),b)}.icon]`,`[${g($(e,2),b)}.icon]`,`[${g($(e,3),b)}.icon]`,`[${g(R(e,1),b)}.icon]`,`[${g(R(e,2),b)}.icon]`,`[${g(R(e,3),b)}.icon]`].join(" "),"今日のn年前",` [${g(oe(e,1),b)}]`].join(` `),`[${g(K(e,1),b)}]←${g(e,b)}→[${g(I(e,1),b)}]`];function rt(e){switch(se(e)){case 0:return"日";case 1:return"月";case 2:return"火";case 3:return"水";case 4:return"木";case 5:return"金";case 6:return"土"}}var L=e=>`connect.sid=${e}`;function ot(e){return e!=null}function st(e){return ot(e)?(e.name===void 0||typeof e.name=="string")&&typeof e.message=="string":!1}function S(e){try{let t=typeof e=="string"?JSON.parse(e):e;return st(t)?t:!1}catch(t){if(t instanceof SyntaxError)return!1;throw t}}function M(e,t){let n=new Error;return n.name=e,n.message=t,n}var j=e=>e.replaceAll(" ","_").toLowerCase();var pe=e=>[...e].map((t,n)=>t===" "?"_":!it.includes(t)||n===e.length-1&&at.includes(t)?encodeURIComponent(t):t).join(""),it='@$&+=:;",',at=':;",';async function Ee(e,t,n){let r=`https://scrapbox.io/api/pages/${e}/${pe(t)}?followRename=${n?.followRename??!0}`,o=await fetch(r,n?.sid?{headers:{Cookie:L(n.sid)}}:void 0);if(!o.ok){let i=S(await o.text());if(!i)throw M("UnexpectedError",`Unexpected error has occuerd when fetching "${r}"`);return{ok:!1,value:i}}let s=await o.json();return{ok:!0,value:s}}async function me(e,t){let{sort:n,limit:r,skip:o}=t??{},s=new URLSearchParams;n!==void 0&&s.append("sort",n),r!==void 0&&s.append("limit",`${r}`),o!==void 0&&s.append("skip",`${o}`);let i=`https://scrapbox.io/api/pages/${e}?${s.toString()}`,a=await fetch(i,t?.sid?{headers:{Cookie:L(t.sid)}}:void 0);if(!a.ok){let p=S(await a.text());if(!p)throw M("UnexpectedError",`Unexpected error has occuerd when fetching "${i}"`);return{ok:!1,value:p}}let c=await a.json();return{ok:!0,value:c}}var ve=(e,t)=>{if(!(e instanceof HTMLDivElement))throw new TypeError(`"${t}" must be HTMLDivElememt but actual is "${e}"`)};var ke=()=>ct(document.getElementsByClassName("status-bar")?.[0],"div.status-bar"),ct=(e,t)=>{if(!!e)return ve(e,t),e};function le(){let e=ke();if(!e)throw new Error("div.status-bar can't be found");let t=document.createElement("div");return e.append(t),{render:(...n)=>{t.textContent="";let r=Ie(...n);r&&t.append(r)},dispose:()=>t.remove()}}function Ie(...e){let t=e.flatMap(r=>{switch(r.type){case"spinner":return[ft()];case"check-circle":return[gt()];case"exclamation-triangle":return[xt()];case"text":return[z(r.text)];case"group":{let o=Ie(...r.items);return o?[o]:[]}}});if(t.length===0)return;if(t.length===1)return t[0];let n=document.createElement("span");return n.classList.add("item-group"),n.append(...t),n}function z(e){let t=document.createElement("span");return t.classList.add("item"),t.append(e),t}function ft(){let e=document.createElement("i");return e.classList.add("fa","fa-spinner"),z(e)}function gt(){let e=document.createElement("i");return e.classList.add("kamon","kamon-check-circle"),z(e)}function xt(){let e=document.createElement("i");return e.classList.add("fas","fa-exclamation-triangle"),z(e)}var ht="4.2.0";async function N(){let t=(await yt())("https://scrapbox.io",{reconnectionDelay:5e3,transports:["websocket"]});return await new Promise((n,r)=>{let o=s=>r(s);t.once("connect",()=>{t.off("disconnect",o),n()}),t.once("disconnect",o)}),t}function yt(){let e=`https://cdnjs.cloudflare.com/ajax/libs/socket.io/${ht}/socket.io.min.js`;if(document.querySelector(`script[src="${e}"]`))return Promise.resolve(window.io);let t=document.createElement("script");return t.src=e,new Promise((n,r)=>{t.onload=()=>n(window.io),t.onerror=o=>r(o),document.head.append(t)})}function P(e,t=9e4){function n(o,s){let i;return new Promise((a,c)=>{let p=d=>{clearTimeout(i),c(new Error(d))};e.emit(o,s,d=>{clearTimeout(i),e.off("disconnect",p),d.error&&c(new Error(JSON.stringify(d.error))),"data"in d?a(d?.data):a(void 0)}),i=setTimeout(()=>{e.off("disconnect",p),c(new Error(`Timeout: exceeded ${t}ms`))},t),e.once("disconnect",p)})}async function*r(...o){let s,i=()=>new Promise(c=>s=c),a=c=>{s?.(c)};for(let c of o)e.on(c,a);try{for(;;)yield await i()}finally{for(let c of o)e.off(c,a)}}return{request:n,response:r}}async function Le(e,t){let n=`https://scrapbox.io/api/projects/${e}`,r=await fetch(n,t?.sid?{headers:{Cookie:L(t.sid)}}:void 0);if(!r.ok){let s=S(await r.json());if(!s)throw M("UnexpectedError",`Unexpected error has occuerd when fetching "${n}"`);return{ok:!1,value:s}}let o=await r.json();return{ok:!0,value:o}}async function Me(e){let t="https://scrapbox.io/api/users/me",n=await fetch(t,e?.sid?{headers:{Cookie:L(e.sid)}}:void 0);if(!n.ok)throw M("UnexpectedError",`Unexpected error has occuerd when fetching "${t}"`);return await n.json()}var W;async function U(){if(W!==void 0)return W;let e=await Me();if(e.isGuest)throw new Error("this script can only be executed by Logged in users");return W=e.id,W}var Ce=new Map;async function H(e){let t=Ce.get(e);if(t!==void 0)return t;let n=await Le(e);if(!n.ok){let{name:o,message:s}=n.value;throw new Error(`${o} ${s}`)}let{id:r}=n.value;return Ce.set(e,r),r}function Ne(e){return e.padStart(8,"0")}function Pe(e){let t=Math.floor(new Date().getTime()/1e3).toString(16),n=Math.floor(16777214*Math.random()).toString(16);return`${Ne(t).slice(-8)}${e.slice(-6)}0000${Ne(n)}`}function Oe(e,t){let n=e.length>t.length,r=n?t:e,o=n?e:t,s=r.length+1,i=r.length+o.length+3,a=new Array(i);a.fill(-1);let c=[];function p(m,E,D){let k=Math.max(E,D),B=k-m;for(;B<r.length&&k<o.length&&r[B]===o[k];)++B,++k;return a[m+s]=c.length,c.push([{x:B,y:k},a[m+(E>D?-1:1)+s]]),k}let d=new Array(i);d.fill(-1);let w=-1,h=o.length-r.length;do{++w;for(let m=-w;m<=h-1;++m)d[m+s]=p(m,d[m-1+s]+1,d[m+1+s]);for(let m=h+w;m>=h+1;--m)d[m+s]=p(m,d[m-1+s]+1,d[m+1+s]);d[h+s]=p(h,d[h-1+s]+1,d[h+1+s])}while(d[h+s]!==o.length);let q=[],v=a[h+s];for(;v!==-1;)q.push(c[v][0]),v=c[v][1];return{from:e,to:t,editDistance:h+w*2,buildSES:function*(){let m=0,E=0;for(let{x:D,y:k}of bt(q))for(;m<D||E<k;)k-D>E-m?(yield{value:o[E],type:n?"deleted":"added"},++E):k-D<E-m?(yield{value:r[m],type:n?"added":"deleted"},++m):(yield{value:r[m],type:"common"},++m,++E)}}}function*Ae(e){let t=[],n=[];function*r(){if(t.length>n.length){for(let o=0;o<n.length;o++)yield Se(t[o],n[o]);for(let o=n.length;o<t.length;o++)yield t[o]}else{for(let o=0;o<t.length;o++)yield Se(t[o],n[o]);for(let o=t.length;o<n.length;o++)yield n[o]}t=[],n=[]}for(let o of e)switch(o.type){case"added":t.push(o);break;case"deleted":n.push(o);break;case"common":yield*r(),yield o;break}yield*r()}function Se(e,t){return{value:e.value,oldValue:t.value,type:"replaced"}}function*bt(e){for(let t=e.length-1;t>=0;t--)yield e[t]}function*He(e,t,{userId:n}){let{buildSES:r}=Oe(e.map(({text:i})=>i),t),o=0,s=e[0].id;for(let i of Ae(r())){switch(i.type){case"added":yield{_insert:s,lines:{id:Pe(n),text:i.value}};continue;case"deleted":yield{_delete:s,lines:-1};break;case"replaced":yield{_update:s,lines:{text:i.value}};break}o++,s=e[o]?.id??"_end"}}var wt=e=>({type:"title",text:e.rows[0].text}),Et=e=>{let{rows:[t,...n]}=e,{indent:r=0,text:o=""}=t??{},s=o.replace(/^\s*code:/,"");return{indent:r,type:"codeBlock",fileName:s,content:n.map(i=>i.text.substring(r+1)).join(` `)}},x=(e,{parseOnNested:t,parseOnQuoted:n,patterns:r})=>(o,s,i)=>{var a,c,p,d,w,h;if(!t&&s.nested)return(a=i==null?void 0:i())!==null&&a!==void 0?a:[];if(!n&&s.quoted)return(c=i==null?void 0:i())!==null&&c!==void 0?c:[];for(let q of r){let v=q.exec(o);if(v===null)continue;let m=o.substring(0,v.index),E=o.substring(v.index+((d=(p=v[0])===null||p===void 0?void 0:p.length)!==null&&d!==void 0?d:0)),D=e((w=v[0])!==null&&w!==void 0?w:"",s);return[...O(m,s),...D,...O(E,s)]}return(h=i==null?void 0:i())!==null&&h!==void 0?h:[]},y=e=>[{type:"plain",raw:e,text:e}],vt=x(y,{parseOnNested:!0,parseOnQuoted:!0,patterns:[/^()(.*)()$/]}),kt=/^>.*$/,Tt=(e,t)=>t.context==="table"?y(e,t):[{type:"quote",raw:e,nodes:O(e.substring(1),{...t,quoted:!0})}],Dt=x(Tt,{parseOnNested:!1,parseOnQuoted:!1,patterns:[kt]}),It=/^\? .+$/,Lt=(e,t)=>t.context==="table"?y(e,t):[{type:"helpfeel",raw:e,text:e.substring(2)}],Mt=x(Lt,{parseOnNested:!1,parseOnQuoted:!1,patterns:[It]}),Ct=/\[\[https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)\]\]/i,Nt=/\[\[https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}\]\]/,Pt=(e,t)=>{if(t.context==="table")return y(e,t);let n=e.substring(2,e.length-2),r=/^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}$/.test(n);return[{type:"strongImage",raw:e,src:r?`${n}/thumb/1000`:n}]},Ot=x(Pt,{parseOnNested:!1,parseOnQuoted:!0,patterns:[Ct,Nt]}),At=/\[[^[\]]*\.icon(?:\*[1-9]\d*)?\]/;function $e(e){return(t,n)=>{if(e==="strongIcon"&&n.context==="table")return y(t,n);let r=e==="icon"?t.substring(1,t.length-1):t.substring(2,t.length-2),o=r.lastIndexOf(".icon"),s=r.substring(0,o),i=s.startsWith("/")?"root":"relative",a=r.substring(o+5,r.length),c=a.startsWith("*")?parseInt(a.substring(1),10):1;return new Array(c).fill({}).map(()=>({path:s,pathType:i,type:e,raw:t}))}}var St=$e("icon"),Ht=x(St,{parseOnNested:!1,parseOnQuoted:!0,patterns:[At]}),$t=/\[\[[^[\]]*\.icon(?:\*\d+)?\]\]/,_t=$e("strongIcon"),Rt=x(_t,{parseOnNested:!1,parseOnQuoted:!0,patterns:[$t]}),jt=/\[\[(?:[^[]|\[[^[]).*?\]*\]\]/,Ut=(e,t)=>t.context==="table"?y(e,t):[{type:"strong",raw:e,nodes:O(e.substring(2,e.length-2),{...t,nested:!0})}],Ft=x(Ut,{parseOnNested:!1,parseOnQuoted:!0,patterns:[jt]}),qt=/\[\$ .+? \]/,Bt=/\[\$ [^\]]+\]/,Yt=(e,t)=>t.context==="table"?y(e,t):[{type:"formula",raw:e,formula:e.substring(3,e.length-(e.endsWith(" ]")?2:1))}],Kt=x(Yt,{parseOnNested:!1,parseOnQuoted:!0,patterns:[qt,Bt]}),zt=/\[[!"#%&'()*+,\-./{|}<>_~]+ (?:\[[^[\]]+\]|[^\]])+\]/,Wt=(e,t)=>{if(t.context==="table")return y(e,t);let n=e.indexOf(" "),r=e.substring(1,n),o=e.substring(n+1,e.length-1),s=new Set(r);if(s.has("*")){let i=r.split("*").length-1;s.delete("*"),s.add(`*-${Math.min(i,10)}`)}return[{type:"decoration",raw:e,rawDecos:r,decos:Array.from(s),nodes:O(o,{...t,nested:!0})}]},Qt=x(Wt,{parseOnNested:!1,parseOnQuoted:!0,patterns:[zt]}),Gt=/`.*?`/,Xt=(e,t)=>t.context==="table"?y(e,t):[{type:"code",raw:e,text:e.substring(1,e.length-1)}],Vt=x(Xt,{parseOnNested:!1,parseOnQuoted:!0,patterns:[Gt]}),Jt=/^[$%] .+$/,Zt=(e,t)=>{var n;if(t.context==="table")return y(e,t);let r=(n=e[0])!==null&&n!==void 0?n:"",o=e.substring(2);return[{type:"commandLine",raw:e,symbol:r,text:o}]},en=x(Zt,{parseOnNested:!1,parseOnQuoted:!1,patterns:[Jt]}),tn=/\[\s+\]/,nn=(e,t)=>t.context==="table"?y(e,t):[{type:"blank",raw:e,text:e.substring(1,e.length-1)}],rn=x(nn,{parseOnNested:!1,parseOnQuoted:!0,patterns:[tn]}),on=/\[https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)(?:\?[^\]\s]+)?(?:\s+https?:\/\/[^\s\]]+)?\]/i,sn=/\[https?:\/\/[^\s\]]+\s+https?:\/\/[^\s\]]+\.(?:png|jpe?g|gif|svg)(?:\?[^\]\s]+)?\]/i,an=/\[https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}(?:\/raw)?(?:\s+https?:\/\/[^\s\]]+)?\]/,cn=/\[https?:\/\/[^\s\]]+\s+https?:\/\/(?:[0-9a-z-]+\.)?gyazo\.com\/[0-9a-f]{32}(?:\/raw)?\]/,un=e=>/^https?:\/\/[^\s\]]+\.(png|jpe?g|gif|svg)(\?[^\]\s]+)?$/i.test(e)||dn(e),dn=e=>/^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}(\/raw)?$/.test(e),pn=(e,t)=>{if(t.context==="table")return y(e,t);let n=e.search(/\s/),r=n!==-1?e.substring(1,n):e.substring(1,e.length-1),o=n!==-1?e.substring(n,e.length-1).trimLeft():"",[s,i]=un(o)?[o,r]:[r,o];return[{type:"image",raw:e,src:/^https?:\/\/([0-9a-z-]\.)?gyazo\.com\/[0-9a-f]{32}$/.test(s)?`${s}/thumb/1000`:s,link:i}]},mn=x(pn,{parseOnNested:!0,parseOnQuoted:!0,patterns:[on,sn,an,cn]}),ln=/\[https?:\/\/[^\s\]]+\s+[^\]]*[^\s]\]/,fn=/\[[^[\]]*[^\s]\s+https?:\/\/[^\s\]]+\]/,gn=/\[https?:\/\/[^\s\]]+\]/,xn=/https?:\/\/[^\s]+/,hn=(e,t)=>{if(t.context==="table")return y(e,t);let n=e.startsWith("[")&&e.endsWith("]")?e.substring(1,e.length-1):e,r=/^https?:\/\/[^\s\]]/.test(n),o=(r?/^https?:\/\/[^\s\]]+/:/https?:\/\/[^\s\]]+$/).exec(n);if((o==null?void 0:o[0])===void 0)return[];let s=r?n.substring(o[0].length):n.substring(0,o.index-1);return[{type:"link",raw:e,pathType:"absolute",href:o[0],content:s.trim()}]},yn=x(hn,{parseOnNested:!0,parseOnQuoted:!0,patterns:[ln,fn,gn,xn]}),_e=/\[([^\]]*[^\s])\s+([NS]\d+(?:\.\d+)?,[EW]\d+(?:\.\d+)?(?:,Z\d+)?)\]/,Re=/\[([NS]\d+(?:\.\d+)?,[EW]\d+(?:\.\d+)?(?:,Z\d+)?)(?:\s+([^\]]*[^\s]))?\]/,bn=e=>{let[t="",n="",r=""]=e.split(","),o=parseFloat(t.replace(/^N/,"").replace(/^S/,"-")),s=parseFloat(n.replace(/^E/,"").replace(/^W/,"-")),i=/^Z\d+$/.test(r)?parseInt(r.replace(/^Z/,""),10):14;return{latitude:o,longitude:s,zoom:i}},wn=(e,t)=>{var n;if(t.context==="table")return y(e,t);let r=(n=e.match(_e))!==null&&n!==void 0?n:e.match(Re);if(r===null)return[];let o=e.startsWith("[N")||e.startsWith("[S"),[,s="",i=""]=o?r:[r[0],r[2],r[1]],{latitude:a,longitude:c,zoom:p}=bn(s),d=i!==""?`https://www.google.com/maps/place/${encodeURIComponent(i)}/@${a},${c},${p}z`:`https://www.google.com/maps/@${a},${c},${p}z`;return[{type:"googleMap",raw:e,latitude:a,longitude:c,zoom:p,place:i,url:d}]},En=x(wn,{parseOnNested:!1,parseOnQuoted:!0,patterns:[_e,Re]}),vn=/\[\/?[^[\]]+\]/,kn=e=>{let t=e.substring(1,e.length-1);return[{type:"link",raw:e,pathType:t.startsWith("/")?"root":"relative",href:t,content:""}]},Tn=x(kn,{parseOnNested:!0,parseOnQuoted:!0,patterns:[vn]}),Dn=/(?:^|\s)#\S+/,In=(e,t)=>{if(t.context==="table")return y(e,t);if(e.startsWith("#"))return[{type:"hashTag",raw:e,href:e.substring(1)}];let n=e.substring(0,1),r=e.substring(1);return[...y(n,t),{type:"hashTag",raw:r,href:r.substring(1)}]},Ln=x(In,{parseOnNested:!0,parseOnQuoted:!0,patterns:[Dn]}),Mn=(e,t,n)=>{var r;return e===""?[]:(r=n==null?void 0:n())!==null&&r!==void 0?r:[]},Cn=(...e)=>(t,n)=>e.reduceRight((r,o)=>()=>o(t,n,r),()=>vt(t,n))(),O=Cn(Mn,Dt,Mt,Vt,en,Kt,rn,Qt,Ot,Rt,Ft,mn,yn,Ht,En,Tn,Ln),Nn=e=>{let{rows:[t,...n]}=e,{indent:r=0,text:o=""}=t??{},s=o.replace(/^\s*table:/,"");return{indent:r,type:"table",fileName:s,cells:n.map(i=>i.text.substring(r+1)).map(i=>i.split(" ").map(a=>O(a,{nested:!1,quoted:!1,context:"table"})))}},Pn=e=>{let{indent:t,text:n}=e.rows[0];return{indent:t,type:"line",nodes:O(n.substring(t),{nested:!1,quoted:!1,context:"line"})}},je=e=>{switch(e.type){case"title":return wt(e);case"codeBlock":return Et(e);case"table":return Nn(e);case"line":return Pn(e)}},Ue=e=>e.split(` `).map(t=>{var n,r,o;return{indent:(o=(r=(n=/^\s+/.exec(t))===null||n===void 0?void 0:n[0])===null||r===void 0?void 0:r.length)!==null&&o!==void 0?o:0,text:t}}),On=(e,t)=>{var n,r;return(e.type==="codeBlock"||e.type==="table")&&t.indent>((r=(n=e.rows[0])===null||n===void 0?void 0:n.indent)!==null&&r!==void 0?r:0)},Fe=(e,t)=>{let n=e[e.length-1];return n!==void 0&&On(n,t)?(n.rows.push(t),e):(e.push({type:/^\s*code:/.test(t.text)?"codeBlock":/^\s*table:/.test(t.text)?"table":"line",rows:[t]}),e)},qe=(e,t)=>{var n;if((n=t.hasTitle)!==null&&n!==void 0?n:!0){let[r,...o]=e;return r===void 0?[]:[{type:"title",rows:[r]},...o.reduce(Fe,[])]}return e.reduce(Fe,[])};function fe(e,t,{userId:n,head:r}){let o=t.flatMap(d=>d.split(` `)),s=[...He(e,o,{userId:n})];(e[0].text!==o[0]||!r.persistent)&&s.push({title:o[0]});let i=e.slice(1,6).map(d=>d.text),a=o.slice(1,6);i.join("")!==a.join("")&&s.push({descriptions:a});let[c,p]=An(o.join(` `));return(r.linksLc.length!==c.length||!r.linksLc.every(d=>c.includes(d)))&&s.push({links:c}),r.image!==p&&s.push({image:p}),s}function An(e){let t=Ue(e),n=qe(t,{hasTitle:!0}).flatMap(i=>{switch(i.type){case"codeBlock":case"title":return[];case"line":case"table":return[je(i)]}}),r=[],o=null,s=i=>{switch(i.type){case"hashTag":r.push(j(i.href));return;case"link":{if(i.pathType!=="relative")return;r.push(j(i.href));return}case"image":case"strongImage":{o??=i.src.endsWith("/thumb/1000")?i.src.replace(/\/thumb\/1000$/,"/raw"):i.src;return}case"strong":case"quote":case"decoration":{for(let a of i.nodes)s(a);return}default:return}};for(let i of Sn(n))s(i);return[r,o]}function*Sn(e){for(let t of e)switch(t.type){case"codeBlock":case"title":continue;case"line":for(let n of t.nodes)yield n;continue;case"table":{for(let n of t.cells)for(let r of n)for(let o of r)yield o;continue}}}async function C(e,t){let n=await Ee(e,t);if(!n.ok)throw new Error(`You have no privilege of editing "/${e}/${t}".`);let{commitId:r,persistent:o,image:s,links:i,lines:a,id:c,pin:p}=n.value;return{commitId:r,pageId:c,persistent:o,image:s,linksLc:i.map(d=>j(d)),pin:p,lines:a}}async function F(e,t,n){return t.length===0?{commitId:n.parentId}:await e("socket.io-request",{method:"commit",data:{kind:"page",...n,changes:t,cursor:null,freeze:!0}})}async function Q(e,t,{project:n,title:r,retry:o=3,parentId:s,...i}){try{s=(await F(e,t,{parentId:s,...i})).commitId}catch{console.log("Faild to push a commit. Retry after pulling new commits");for(let c=0;c<o;c++){let{commitId:p}=await C(n,r);s=p;try{s=(await F(e,t,{parentId:s,...i})).commitId,console.log("Success in retrying");break}catch{continue}}throw Error("Faild to retry pushing.")}return s}var Be=()=>Number.MAX_SAFE_INTEGER-Math.floor(Date.now()/1e3);async function ge(e,t,n){let[r,o,s]=await Promise.all([C(e,t),H(e),U()]),i=r,a=await N();try{let{request:c}=P(a);for(let p=0;p<3;p++)try{let d=n(i.lines),w=d instanceof Promise?await d:d,h=fe(i.lines,w,{userId:s,head:i});await F(c,h,{parentId:i.commitId,projectId:o,pageId:i.pageId,userId:s});break}catch{if(p===2)throw Error("Faild to retry pushing.");console.log("Faild to push a commit. Retry after pulling new commits");try{i=await C(e,t)}catch(w){throw w}}}finally{a.disconnect()}}async function xe(e,t,n){let[r,o,s]=await Promise.all([C(e,t),H(e),U()]);if(r.pin>0||!r.persistent&&!(n?.create??!1))return;let i={parentId:r.commitId,projectId:o,pageId:r.pageId,userId:s,project:e,title:t},a=await N(),{request:c}=P(a);if(!r.persistent){let p=await Q(c,[{title:t}],i);i.parentId=p}try{await Q(c,[{pin:Be()}],i)}finally{a.disconnect()}}async function he(e,t){let[n,r,o]=await Promise.all([C(e,t),H(e),U()]);if(n.pin==0||!n.persistent)return;let s={parentId:n.commitId,projectId:r,pageId:n.pageId,userId:o,project:e,title:t},i=await N(),{request:a}=P(i);try{await Q(a,[{pin:0}],s)}finally{i.disconnect()}}var ye=e=>new Promise(t=>setTimeout(()=>t(),e));async function*Ye(e){for await(let t of Ke(e))!/\d{4}\/\d{2}\/\d{2}/.test(t.title)||(yield t)}async function*Ke(e,t=0){let{count:n,pages:r}=await Hn(e,t);for(let o of r)o.pin!==0&&(yield o);(r.at(-1)?.pin??0)!==0&&(yield*Ke(e,t+1e3))}async function Hn(e,t){let n=await me(e,{limit:1e3,skip:t});if(!n.ok){let r=new Error;throw r.name=n.value.name,r.message=n.value.message,r}return n.value}function Ma(e,t=24*3600*1e3){let n=()=>scrapbox.Project.name===e?$n(e,t):We();n(),scrapbox.addListener("project:changed",n)}var ze;async function $n(e,t){We(),await Qe(e,new Date),ze=setInterval(()=>Qe(e,new Date),t)}function We(){clearInterval(ze)}async function Qe(e,t){let{render:n,dispose:r}=le();try{n({type:"spinner"},{type:"text",text:"unpin other diary pages..."});for await(let{title:p}of Ye(e))p!==_n(t)&&await he(e,p);let[o,s,i]=de(t);n({type:"spinner"},{type:"text",text:`pin "/${e}/${o}"...`}),await xe(e,o,{create:!0});let a=s.split(` `),c=i.split(` `);n({type:"spinner"},{type:"text",text:`format "/${e}/${o}"...`}),await ge(e,o,p=>[p[0].text,...be(p.slice(1).map(d=>d.text),a,c)]),n({type:"check-circle"},{type:"text",text:`Pinned "/${e}/${o}".`})}catch(o){n({type:"exclamation-triangle"},{type:"text",text:o instanceof Error?`${o.name} ${o.message}`:"Unknown error! (see developper console)"}),console.error(o)}finally{await ye(1e3),r()}}function _n(e){return`${e.getFullYear()}/${Ge(e.getMonth()+1)}/${Ge(e.getDate())}`}function Ge(e){return String(e).padStart(2,"0")}export{Ma as launch,Qe as pinDiary};

UserScript