Download mathly2/static/media.js from wzender/mathly: direct link, hf CLI and curl.
- Browser
- Download file 3 kB
-
https://huggingface.co/spaces/wzender/mathly/resolve/main/mathly2/static/media.js
- Command line
-
hf download hf://spaces/wzender/mathly/mathly2/static/media.js
-
curl -L -o media.js https://huggingface.co/spaces/wzender/mathly/resolve/main/mathly2/static/media.js
3 kB
| // Pictures in the browser: reading a private picture through the server (a plain <img src> cannot send the sign-in), shrinking a photo before | |
| // it is sent, uploading it, and telling a phone from a computer. | |
| import { token } from "./session.js"; | |
| import { ApiError } from "./api.js"; | |
| const cache = new Map(); | |
| /** A picture's bytes as a blob URL, fetched with the sign-in (the server is the only door to pictures). Rejects with ApiError (410: deleted). */ | |
| export function pictureUrl(id) { | |
| if (!cache.has(id)) { | |
| cache.set(id, (async () => { | |
| const response = await fetch(`/api/pictures/${encodeURIComponent(id)}`, { headers: { Authorization: "Bearer " + await token() } }); | |
| if (!response.ok) { cache.delete(id); throw new ApiError(response.status, response.status === 410 ? "讛转诪讜谞讛 谞诪讞拽讛" : "诇讗 讛爪诇讞谞讜 诇驻转讜讞 讗转 讛转诪讜谞讛"); } | |
| return URL.createObjectURL(await response.blob()); | |
| })()); | |
| } | |
| return cache.get(id); | |
| } | |
| /** A photo shrunk to at most `side` pixels and re-encoded as JPEG (this also drops its metadata). Falls back to the file itself when the browser | |
| * cannot decode it, and the server then decides. */ | |
| export async function prepare(file, side = 2000) { | |
| try { | |
| const bitmap = await createImageBitmap(file, { imageOrientation: "from-image" }); | |
| const scale = Math.min(1, side / Math.max(bitmap.width, bitmap.height)); | |
| const canvas = Object.assign(document.createElement("canvas"), { width: Math.round(bitmap.width * scale), height: Math.round(bitmap.height * scale) }); | |
| const context = canvas.getContext("2d"); | |
| context.fillStyle = "#fff"; | |
| context.fillRect(0, 0, canvas.width, canvas.height); | |
| context.drawImage(bitmap, 0, 0, canvas.width, canvas.height); | |
| const blob = await new Promise(done => canvas.toBlob(done, "image/jpeg", 0.88)); | |
| return blob || file; | |
| } catch { return file; } | |
| } | |
| /** Send picture bytes (a Blob or File) to `url`; resolves to the JSON answer. */ | |
| export async function sendPicture(url, blob) { | |
| const response = await fetch(url, { method: "POST", headers: { Authorization: "Bearer " + await token(), "Content-Type": blob.type || "image/jpeg" }, body: blob }); | |
| let data = null; | |
| try { data = await response.json(); } catch { /* no body */ } | |
| if (!response.ok) throw new ApiError(response.status, (data && typeof data.detail === "string" && data.detail) || "诪砖讛讜 讛砖转讘砖, 谞住讜 砖讜讘"); | |
| return data; | |
| } | |
| /** A phone or tablet (a touch screen): where the camera opens directly and notifications are offered. */ | |
| export const isTouch = () => !!window.matchMedia?.("(pointer: coarse)").matches; | |
| /** This browser's own id, so a photo request knows which device asked and the phone does not ask itself. */ | |
| export function deviceId() { | |
| try { | |
| let id = localStorage.getItem("mathly2.device"); | |
| if (!id) { id = crypto.randomUUID(); localStorage.setItem("mathly2.device", id); } | |
| return id; | |
| } catch { return "device-" + Math.random().toString(36).slice(2); } | |
| } | |