generated at
scrapbox-reference-maker
web pageをscrapするbookmarklet

使い方
bookmarkletを押すとdialogが出てくる
こういうの
タイトル・URL
変更する必要があればいじる
説明文
textarea
一言メモを残すように促す
project
scrapを作成する先のproject
defaultは takker
confirm を押すと作成される

2021-02-03 18:38:00 選択範囲を引用として取り込むようにした

bookmarklet.js
javascript:(()=>{let s=document.createElement("script");s.src='https://scrapbox.io/api/code/takker/scrapbox-reference-maker/script2.js';document.body.appendChild(s);})()

Native JS版
script2.js
javascript: (() => { const dialogId = 'scrapbox-reference-maker'; const dialogHTML = `<dialog id="${dialogId}"> <form method="dialog"> <p> <label>Title: <input name="title" type="text" ></input> </label> </p> <p> <label>Project: <input name="project" type="text" ></input> </label> </p> <p> <label>Description: <textarea name="description"></textarea> </label> </p> <menu> <button value="cancel">Cancel</button> <button value="default">Confirm</button> </menu> </form></dialog>`; document.getElementById(dialogId)?.remove(); document.body.insertAdjacentHTML('beforeend',dialogHTML); // 選択範囲を取得する const quote = window.getSelection().toString().trim() .split(/\n\r?|\r?\n|\r|\f/g) // 改行区切りで配列化 .filter(line => line !== '') // 空行は削除 .map(line => `>${line}`); // 引用記号 // utilities const ng = text => text.trim().replace(/[\[\]\n]/g, ' '); const e = text => encodeURIComponent(text); const zero = n => String(n).padStart(2, '0'); const today = (d => `[${d.getFullYear()}-${zero(d.getMonth()+1)}-${zero(d.getDate())}] ${zero(d.getHours())}:${zero(d.getMinutes())}:${zero(d.getSeconds())}`)(new Date()); const dialog = document.getElementById(dialogId); dialog.querySelector('input[name="title"]').value = ng(document.title).trim(); dialog.querySelector('input[name="project"]').value = 'takker'; dialog.addEventListener('close', () => { if (dialog.returnValue !== 'default') return; const elements = dialog.firstElementChild.elements; const title = elements['title'].value; const project = elements['project'].value; const description = dialog.querySelector('textarea').value; // scrap pageを作成する const lines = [ `[${window.location.href} ${title}]`, ...quote, description, '', `Added on ${today}`, ]; window.open(`https://scrapbox.io/${project}/${e(ng(title))}?body=${e(lines.join('\n'))}`); }); dialog.showModal(); })();

hr
↓うまく動かないので放置
Custom ElementsShadow DOMを使いたかった
使用すると、Uncaught DOMException: Operation is not supportedというerrorが出て、DOMが正常に反映されない
↓のanswerのcodeに沿えば直るかもしれないが、流石に面倒になってきたので止めた
また独自にeventを生やす方法もよくわからなかった
cf.
本当にweb componentで作りたいときは、Slim.jsを使ったほうが良さそう。
script.js
javascript: (() => { const dialogId = 'scrapbox-reference-maker'; const dialogHTML = ` <form method="dialog"> <label>Title: <input id="title" type="text" ></input> </label> <label>Project: <input id="project" type="text" ></input> </label> <label>Description: <textarea id="description"></textarea> </label> <menu> <button value="cancel">Cancel</button> <button value="default">Confirm</button> </menu> </form>`; customElements.define(dialogId, class extends HTMLDialogElement { constructor() { super(); // dialogのDOMを作成 const shadowRoot = this.attachShadow({mode: 'open'}); const fragment = document.createDocumentFragment(); fragment.innerHTML = dialogHTML; // 要素を取得 this.titleForm = fragment.querySelector('#title'); this.projectForm = fragment.querySelector('#project'); this.descriptionForm = fragment.querySelector('#description'); // 属性から初期値を設定する const title = this.getAttribute('title'); const project = this.getAttribute('project'); const url = this.getAttribute('url'); const onConfirm = this.getAttribute('onConfirm'); this.titleForm.textContent = title; this.projectForm.textContent = project; // confirmを押されたらscrapのdataを返す this.addEventListener('close', () =>{ if (this.returnValue === "cancel") return; onConfirm({ title: this.titleForm.textContent, url: url, project: this.projectForm.textContent, description: this.descriptionForm.textContent}); }); shadowRoot.appendChild(fragment); this.rendered = false; } static get observedAttributes() { return ['title','project','url']; } attributeChangedCallback(attrName, oldVal, newVal) { if (oldVal !== newVal) { console.log('attributeChangedCallback', newVal); this.root.querySelector('.name').textContent = newVal; } } },{extends: 'dialog'}); // utilities const ng = text => text.trim().replace(/[\[\]\n]/g, ' '); const e = text => encodeURIComponent(text); const zero = n => String(n).padStart(2, '0'); const today = (d => `${d.getFullYear()}-${zero(d.getMonth()+1)}-${zero(d.getDate())} ${zero(d.getHours())}:${zero(d.getMinutes())}:${zero(d.getSeconds())}`)(new Date()); document.body.insertAdjacentHTML('beforeend', ` <dialog is="${dialogId}" id="${dialogId}" title="${ng(document.title).trim()}" project="takker" url="${window.location.href}"></dialog> `); let result = {}; const dialog = document.getElementById(dialogId); dialog.setAttribute('onConfirm', ({title,url,project,description})=>{ // scrap pageを作成する const lines = [ `[${url} ${title}]`, '', description, '', `Added on [${today}]`, ]; window.open(`https://scrapbox.io/${project}/${e(ng(title))}?body=${e(lines.join('\n'))}`); }); dialog.showModal(); })();

#2021-02-03 18:37:48
#2020-11-23 06:29:55