// ==UserScript== // @name Bookmark to Cosense // @namespace https://scrapbox.io/directory // @match *://*/** // @version 1.1.1 // @author OKAMURA Naoki aka nyarla <nyarla@kalaclista.com> // @description The UserScript for bookmark all website to Cosense (formaly known Scrapbox) // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_xmlhttpRequest // ==/UserScript== const cosenseId = GM_getValue('COSENSE_ID'); const getProperty = (getters) => { for (const getter of getters) { const [ selector, access ] = getter; const el = document.querySelector(selector); const text = el ? access(el) : ''; if (text) { return text; } } return ''; }; const makeCosenseData = () => { const content = el => el.getAttribute('content'); const text = el => el.textContent; const title = getProperty([ // by Open Graph Protocol ['meta[property="og:title"]', content], ['meta[name="og:title"]', content], // this is mistake in website, but supported. // by Twitter Card ['meta[name="twitter:title"]', content], ['meta[property="twitter:title"]', content], // that is mistake again in website, but supported. // by Microformat v2 ['.h-entry .p-pname', text], // the current microformat is version 2. // by Microformat v1 ['.hentry .entry-title', text], // but support microformat version 1 too. // by title ['title', text], // finally, fallback to getting from title element ]).replace(/[\r\n]/g, ""); const summary = getProperty([ // by Open Graph Protocol ['meta[property="og:description"]', content], ['meta[name="og:description"]', content], // this is mistake in website, but supported. // by Twitter card ['meta[name="twitter:description"]', content], ['meta[property="twitter:description"]', content], // this is mistake again in website, but supported. // by Microformat v2 ['.h-entry .p-summary', text], // the current microformat is version 2. // by Microformat v1 ['.hentry .entry-summary', text], // but support microformat version 1 too. // fallback to meta[name="description"] ['meta[name="description"]', content], // finally, fallback to old style meta[name="description"] ]); const website = getProperty([ ['meta[property="og:site_name"]', content], // get website name from og:site_name. ['meta[name="og:site_name"]', content], // this is mistake in website, but supported. ]); const href = getProperty([ ['meta[property="og:url"]', content], // get canonical url from ogp, ['meta[name="og:url"]', content], // this is mistake in website, but supported. ['link[rel="canonical"]', el => el.getAttribute('href')], // use rel="canonial" href. ]) || location.href; // finally, use location.href. // get favicon from hidden api by google const icon = `https://t3.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=${encodeURIComponent(href)}&size=128#.png`; const date = new Date().toISOString().split(/T/)[0]; const favicon = `[!<> [${icon}]]`; const link = `[${title} ${href}]`; const quote = `> ${summary ? summary : title}\n[> [${decodeURIComponent(href)}]] `; const body = ` Date: ${date} ${quote.replace(/\n/g, "\n> ")} ${favicon} `; return [title, body]; }; const bookmarkToCosense = () => { if (! cosenseId) { window.alert('The value of `COSENSE_ID` is not set. Please set `COSENSE_ID` to your UserScript manager'); return; } const [ title, body ] = makeCosenseData(); GM_xmlhttpRequest({ method: 'GET', url: `https://scrapbox.io/api/pages/${cosenseId}/${encodeURIComponent(title)}`, onload: (res) => { const json = JSON.parse(res.responseText); if (json.lines.length > 1) { location.href = `https://scrapbox.io/${cosenseId}/${encodeURIComponent(title)}`; return; } location.href = `https://scrapbox.io/${cosenseId}/${encodeURIComponent(title)}?body=${encodeURIComponent(body)}`; }, }); }; const popupCosenseData = () => { window.alert(makeCosenseData().join("\n")); }; GM_registerMenuCommand('Bookmark to Cosense', bookmarkToCosense); GM_registerMenuCommand('Make Cosense data', popupCosenseData);