右下にたねのぶを出す
https://gyazo.com/fe11c4c4b745c176df7ac5ff67068236
code:script.js
const elementApp = document.querySelector('.app');
document.addEventListener('keydown', ()=> {
if (Math.floor(Math.random() * 100) < 95) {
elementApp.classList.add('mtane0412-speak');
} else {
elementApp.classList.add('mtane0412-speak-blink');
}
});
document.addEventListener('keyup', ()=> {
elementApp.classList.remove('mtane0412-speak');
elementApp.classList.remove('mtane0412-speak-blink');
});
code:script.js
/* MIT License Copyright (c) 2020 ci7lus */
import { importExternalJs } from "/api/code/ci7lus/userscript-utils/import-external-js.js"
import { importStyle } from "/api/code/ci7lus/userscript-utils/import-style.js"
await importExternalJs(
)
await importExternalJs(
)
await importStyle(
)
import "/api/code/ci7lus/pdfの全てのページをGyazoにアップロードしてScrapboxに貼り付けるUserScript/throttle.js"
import { insertText } from "/api/code/customize/scrapbox-insert-text/script.js"
scrapbox.PageMenu.addMenu({
title: "upload-pdf",
image: "/assets/img/logo_cut.svg",
onClick: async () => {
const input = document.createElement("input")
input.type = "file"
input.accept = "application/pdf,.pdf"
input.addEventListener("change", async () => {
if (input.files.length === 0) return
const file = input.files0 const filename = file.name
console.log(file)
// 進捗表示エリア
const progressArea = document.createElement("div")
progressArea.style =
"position: fixed; top: 0; right: 0; margin: 1rem; padding: 1rem; background: #FFF; color: 000; z-index: 9999;" progressArea.innerText = "reading..."
document.body.appendChild(progressArea)
// "Error: The Array.prototype contains unexpected enumerable properties: getIndexByTitleLc; thus breaking e.g. for...in iteration of Arrays."
// Array.prototype に変なプロパティが付いてると落ちる謎仕様になっていたのでアップロード中は削除する
const getIndexByTitleLc = Array.prototype.getIndexByTitleLc
delete Array.prototype.getIndexByTitleLc
try {
// File/BlobをArrayBufferに変換
const pdfObj = await new Promise((res, rej) => {
const reader = new FileReader()
reader.onerror = rej
reader.onload = () => {
res(reader.result)
}
reader.readAsArrayBuffer(file)
})
// cors制限のためcmapsはstorage.googleapis.comに上げたものを用いる
const pdf = await pdfjsLib.getDocument({
data: pdfObj,
cMapUrl:
cMapPacked: true,
}).promise
const metadata = await pdf.getMetadata()
console.log(metadata)
const isBigImage = window.pdfscrap_big || false
progressArea.innerText = "pdf file loaded. Uploading to gyazo..."
let progress = 0
const updateProgressText = () => {
progressArea.innerText = `Uploading... (${progress++}/${
pdf.numPages
})`
}
const project = await fetch(
https://scrapbox.io/api/projects/${scrapbox.Project.name}
)
const { gyazoTeamsName } = await project.json()
if (gyazoTeamsName) {
alert(
"Gyazo Teamsを使ったことがないのでアップロードがおかしくなるかもしれません(可能であればTwitter@ci7lusまで動作確認報告お願いします)"
)
}
const gyazoOAuthToken = await fetch(
gyazoTeamsName || ""
}`,
{
headers: {
accept: "application/json, text/plain, */*",
},
method: "GET",
}
)
const { token } = await gyazoOAuthToken.json()
const uploadPage = async (page) => {
const pdfPage = await pdf.getPage(page)
const viewport = pdfPage.getViewport({
scale: window.devicePixelRatio || 1.5,
})
const canvas = document.createElement("canvas")
const ctx = canvas.getContext("2d")
const renderContext = {
canvasContext: ctx,
viewport: viewport,
}
canvas.height = viewport.height
canvas.width = viewport.width
await pdfPage.render(renderContext).promise
/** @type {string} */
let imageUrl
if (token) {
console.info("Gyazo OAuth 経由でアップロードします")
const imageBlob = await new Promise((res) =>
canvas.toBlob(res, "image/jpeg", 0.95)
)
const formData = new FormData()
formData.append("imagedata", imageBlob)
formData.append("access_token", token)
formData.append("referer_url", location.href)
formData.append("title", filename)
while (true) {
// 登録順序を保証する
if (page - 1 <= progress) {
break
}
await new Promise((res) => setTimeout(() => res(), 100))
}
headers: {
accept: "application/json, text/plain, */*",
},
referrerPolicy: "same-origin",
body: formData,
method: "POST",
mode: "cors",
credentials: "omit",
})
const { permalink_url } = await upload.json()
imageUrl = permalink_url
} else {
console.info("Gyazo Easy_Auth 経由でアップロードします")
const dataUrl = canvas.toDataURL("image/jpeg")
const formData = new FormData()
formData.append("image_url", dataUrl)
formData.append(
"client_id",
"5a56f659c139358389c8c4838555135907d7edfbb98b9465aa6c51200e11dec5"
)
formData.append("referer_url", location.href)
formData.append("title", filename)
const easyAuth = await fetch(
https://upload.gyazo.com/api/upload/easy_auth,
{
method: "POST",
mode: "cors",
credentials: "include",
body: formData,
}
)
const uploadResult = await easyAuth.json()
while (true) {
// 登録順序を保証する
if (page - 1 <= progress) {
break
}
await new Promise((res) => setTimeout(() => res(), 100))
}
const getImage = await fetch(uploadResult.get_image_url, {
mode: "cors",
credentials: "include",
})
imageUrl = getImage.url
}
updateProgressText()
console.log(page${page} -> ${imageUrl})
return imageUrl
}
const pages = await throttle.all(
.map((i) => i + 1)
.map((page) => () => uploadPage(page))
)
progressArea.innerText = "done"
const urls = pages.map((url) =>
isBigImage ? [[${url}]] : [${url}]
)
urls.unshift(file.name)
insertText({
text: urls.join("\n") + "\n",
})
} catch (e) {
console.log("failed", file, e)
alert(failed to load: ${filename})
} finally {
document.body.removeChild(progressArea)
Array.prototype.getIndexByTitleLc = getIndexByTitleLc
}
})
input.click()
},
})
code:style.css
:root {
}
/* 画像プリロード */
body::after{
position:absolute; width:0; height:0; overflow:hidden; z-index:-1;
content:var(--eyeOpen-mouthClose) var(--eyeOpen-mouthOpen) var(--eyeClose-mouthClose) var(--eyeClose-mouthOpen) var(--surprise);
}
@keyframes mtane0412-blink {
0% { background-image: var(--eyeOpen-mouthClose); }
69% { background-image: var(--eyeOpen-mouthClose); }
70% { background-image: var(--eyeClose-mouthClose); }
80% { background-image: var(--eyeClose-mouthClose); }
81% { background-image: var(--eyeOpen-mouthClose); }
100% { background-image: var(--eyeOpen-mouthClose); }
}
@media only screen and (min-width: 480px) {
.app::after {
content: "";
background-image: var(--eyeOpen-mouthClose);
background-size: 256px 256px;
display: block;
width: 256px;
height: 256px;
position: fixed;
right: -70px;
bottom: -120px;
animation: 5s mtane0412-blink linear infinite;
}
}
.app:active::after {
background-image: var(--surprise);
bottom: -100px;
animation: none;
}
.app.mtane0412-speak::after {
/* ぴょこぴょこ用 */
background-image: var(--eyeOpen-mouthOpen);
animation: none;
}
.app.mtane0412-speak-blink::after {
/* ぴょこぴょこ用 */
background-image: var(--eyeClose-mouthOpen);
animation: none;
}