-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.ts
205 lines (181 loc) · 4.89 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import type { Readable } from "svelte/store"
import { browser, toReadable, toWritable } from "@sveu/shared"
import type { Base64ObjectOptions, ToDataURLOptions } from "../utils"
const DEFAULT_SERIALIZER = {
array: (v: unknown[]) => JSON.stringify(v),
object: (v: Record<string, unknown>) => JSON.stringify(v),
set: (v: Set<unknown>) => JSON.stringify(Array.from(v)),
map: (v: Map<string, unknown>) => JSON.stringify(Object.fromEntries(v)),
null: () => "",
}
/**
* Get the serialization function for a given target
*
* @param target - The target to get the serialization function for
*
* @returns The serialization function for the target
*/
function get_serialization<T extends object>(target: T) {
if (!target) return DEFAULT_SERIALIZER.null
if (target instanceof Map) return DEFAULT_SERIALIZER.map
else if (target instanceof Set) return DEFAULT_SERIALIZER.set
else if (Array.isArray(target)) return DEFAULT_SERIALIZER.array
else return DEFAULT_SERIALIZER.object
}
/**
* Load an image and return a promise that resolves when the image is loaded
*
* @param img - The image to load
*/
function img_loaded(img: HTMLImageElement) {
return new Promise<void>((resolve, reject) => {
if (!img.complete) {
img.onload = () => {
resolve()
}
img.onerror = reject
} else {
resolve()
}
})
}
/**
* Convert a blob to a base64 string
*
* @param blob - The blob to convert
*
* @returns The base64 string
*/
function blob_to_base64(blob: Blob) {
return new Promise<string>((resolve, reject) => {
const fr = new FileReader()
fr.onload = (e) => {
resolve(e.target?.result as string)
}
fr.onerror = reject
fr.readAsDataURL(blob)
})
}
/**
* Convert a value to a base64 string
*
* @param target - The value to convert
*
* @param options - The options to use
* - `serializer` - The serializer to use. Only used if the target is an object
* - `type` - The MIME type to use. Only used if the target is a canvas or image
* - `quality` - The image quality to use. Only used if the target is a canvas or image
*
* @example
* ```ts
* const result = base64('Hello world')
* ```
*
* @example
* ```ts
* const result = base64(new Blob(['Hello world']))
* ```
*
* @example
* ```ts
* const result = base64(new ArrayBuffer(10))
* ```
*
* @returns The base64 string readable store
*/
export function base64(target: string): Readable<string>
export function base64(target: Blob): Readable<string>
export function base64(target: ArrayBuffer): Readable<string>
export function base64(
target: HTMLCanvasElement,
options?: ToDataURLOptions
): Readable<string>
export function base64(
target: HTMLImageElement,
options?: ToDataURLOptions
): Readable<string>
export function base64<T extends Record<string, unknown>>(
target: T,
options?: Base64ObjectOptions<T>
): Readable<string>
export function base64<T extends Map<string, unknown>>(
target: T,
options?: Base64ObjectOptions<T>
): Readable<string>
export function base64<T extends Set<unknown>>(
target: T,
options?: Base64ObjectOptions<T>
): Readable<string>
export function base64<T>(
target: T[],
options?: Base64ObjectOptions<T[]>
): Readable<string>
export function base64(target: unknown, options?: any) {
const base64 = toWritable("")
function execute() {
if (!browser) return
new Promise<string>((resolve, reject) => {
try {
if (target == null) resolve("")
else if (typeof target === "string") {
resolve(
blob_to_base64(
new Blob([target], { type: "text/plain" })
)
)
} else if (target instanceof Blob) {
resolve(blob_to_base64(target))
} else if (target instanceof ArrayBuffer) {
resolve(
window.btoa(
String.fromCharCode(...new Uint8Array(target))
)
)
} else if (target instanceof HTMLCanvasElement) {
resolve(target.toDataURL(options?.type, options?.quality))
} else if (target instanceof HTMLImageElement) {
const img = target.cloneNode(false) as HTMLImageElement
img.crossOrigin = "Anonymous"
img_loaded(img)
.then(() => {
const canvas = document.createElement("canvas")
const ctx = canvas.getContext("2d")
canvas.width = img.width
canvas.height = img.height
ctx?.drawImage(
img,
0,
0,
canvas.width,
canvas.height
)
resolve(
canvas.toDataURL(
options?.type,
options?.quality
)
)
})
.catch(reject)
} else if (typeof target === "object") {
const serializer =
options?.serializer || get_serialization(target)
const serialized = serializer(target)
return resolve(
blob_to_base64(
new Blob([serialized], {
type: "application/json",
})
)
)
} else {
reject(new Error("target is unsupported types"))
}
} catch (error) {
reject(error)
}
}).then((res) => base64.set(res))
}
execute()
return toReadable(base64)
}