browser-extension-utils 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -3
- package/lib/add-element.ts +38 -0
- package/lib/create-element.ts +11 -0
- package/lib/dom-utils.ts +232 -0
- package/lib/global.d.ts +154 -0
- package/lib/index.ts +358 -0
- package/lib/set-attributes.ts +42 -0
- package/lib/userscript.ts +94 -0
- package/package.json +20 -31
- package/lib/index.d.ts +0 -207
- package/lib/index.js +0 -548
- package/lib/userscript.js +0 -86
package/lib/index.js
DELETED
|
@@ -1,548 +0,0 @@
|
|
|
1
|
-
export const doc = document
|
|
2
|
-
|
|
3
|
-
export const win = globalThis
|
|
4
|
-
|
|
5
|
-
export const uniq = (array) => [...new Set(array)]
|
|
6
|
-
|
|
7
|
-
// Polyfill for String.prototype.replaceAll()
|
|
8
|
-
// eslint-disable-next-line no-use-extend-native/no-use-extend-native
|
|
9
|
-
if (typeof String.prototype.replaceAll !== "function") {
|
|
10
|
-
// eslint-disable-next-line no-use-extend-native/no-use-extend-native, no-extend-native
|
|
11
|
-
String.prototype.replaceAll = String.prototype.replace
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export const toCamelCase = function (text) {
|
|
15
|
-
return text.replaceAll(/^([A-Z])|[\s-_](\w)/g, function (match, p1, p2) {
|
|
16
|
-
if (p2) return p2.toUpperCase()
|
|
17
|
-
return p1.toLowerCase()
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export const $ = (selectors, element) =>
|
|
22
|
-
(element || doc).querySelector(selectors) || undefined
|
|
23
|
-
export const $$ = (selectors, element) => [
|
|
24
|
-
...(element || doc).querySelectorAll(selectors),
|
|
25
|
-
]
|
|
26
|
-
export const querySelector = $
|
|
27
|
-
export const querySelectorAll = $$
|
|
28
|
-
|
|
29
|
-
export const getRootElement = (type) =>
|
|
30
|
-
type === 1
|
|
31
|
-
? doc.head || doc.body || doc.documentElement
|
|
32
|
-
: type === 2
|
|
33
|
-
? doc.body || doc.documentElement
|
|
34
|
-
: doc.documentElement
|
|
35
|
-
|
|
36
|
-
export const createElement = (tagName, attributes) =>
|
|
37
|
-
setAttributes(doc.createElement(tagName), attributes)
|
|
38
|
-
|
|
39
|
-
export const addElement = (parentNode, tagName, attributes) => {
|
|
40
|
-
if (typeof parentNode === "string") {
|
|
41
|
-
return addElement(null, parentNode, tagName)
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
if (!tagName) {
|
|
45
|
-
return
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!parentNode) {
|
|
49
|
-
parentNode = /^(script|link|style|meta)$/.test(tagName)
|
|
50
|
-
? getRootElement(1)
|
|
51
|
-
: getRootElement(2)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (typeof tagName === "string") {
|
|
55
|
-
const element = createElement(tagName, attributes)
|
|
56
|
-
parentNode.append(element)
|
|
57
|
-
return element
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// tagName: HTMLElement
|
|
61
|
-
setAttributes(tagName, attributes)
|
|
62
|
-
parentNode.append(tagName)
|
|
63
|
-
return tagName
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export const addStyle = (styleText) => {
|
|
67
|
-
const element = createElement("style", { textContent: styleText })
|
|
68
|
-
getRootElement(1).append(element)
|
|
69
|
-
return element
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export const addEventListener = (element, type, listener, options) => {
|
|
73
|
-
if (!element) {
|
|
74
|
-
return
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
if (typeof type === "object") {
|
|
78
|
-
for (const type1 in type) {
|
|
79
|
-
if (Object.hasOwn(type, type1)) {
|
|
80
|
-
element.addEventListener(type1, type[type1])
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
} else if (typeof type === "string" && typeof listener === "function") {
|
|
84
|
-
element.addEventListener(type, listener, options)
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export const removeEventListener = (element, type, listener, options) => {
|
|
89
|
-
if (!element) {
|
|
90
|
-
return
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (typeof type === "object") {
|
|
94
|
-
for (const type1 in type) {
|
|
95
|
-
if (Object.hasOwn(type, type1)) {
|
|
96
|
-
element.removeEventListener(type1, type[type1])
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
} else if (typeof type === "string" && typeof listener === "function") {
|
|
100
|
-
element.removeEventListener(type, listener, options)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export const getAttribute = (element, name) =>
|
|
105
|
-
element && element.getAttribute ? element.getAttribute(name) : undefined
|
|
106
|
-
export const setAttribute = (element, name, value) =>
|
|
107
|
-
element && element.setAttribute
|
|
108
|
-
? element.setAttribute(name, value)
|
|
109
|
-
: undefined
|
|
110
|
-
export const removeAttribute = (element, name) =>
|
|
111
|
-
element && element.removeAttribute ? element.removeAttribute(name) : undefined
|
|
112
|
-
|
|
113
|
-
export const setAttributes = (element, attributes) => {
|
|
114
|
-
if (element && attributes) {
|
|
115
|
-
for (const name in attributes) {
|
|
116
|
-
if (Object.hasOwn(attributes, name)) {
|
|
117
|
-
const value = attributes[name]
|
|
118
|
-
if (value === undefined) {
|
|
119
|
-
continue
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (/^(value|textContent|innerText)$/.test(name)) {
|
|
123
|
-
element[name] = value
|
|
124
|
-
} else if (/^(innerHTML)$/.test(name)) {
|
|
125
|
-
element[name] = createHTML(value)
|
|
126
|
-
} else if (name === "style") {
|
|
127
|
-
setStyle(element, value, true)
|
|
128
|
-
} else if (/on\w+/.test(name)) {
|
|
129
|
-
const type = name.slice(2)
|
|
130
|
-
addEventListener(element, type, value)
|
|
131
|
-
} else {
|
|
132
|
-
setAttribute(element, name, value)
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return element
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
export const addAttribute = (element, name, value) => {
|
|
142
|
-
const orgValue = getAttribute(element, name)
|
|
143
|
-
if (!orgValue) {
|
|
144
|
-
setAttribute(element, name, value)
|
|
145
|
-
} else if (!orgValue.includes(value)) {
|
|
146
|
-
setAttribute(element, name, orgValue + " " + value)
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
export const addClass = (element, className) => {
|
|
151
|
-
if (!element || !element.classList) {
|
|
152
|
-
return
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
element.classList.add(className)
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export const removeClass = (element, className) => {
|
|
159
|
-
if (!element || !element.classList) {
|
|
160
|
-
return
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
element.classList.remove(className)
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
export const hasClass = (element, className) => {
|
|
167
|
-
if (!element || !element.classList) {
|
|
168
|
-
return false
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return element.classList.contains(className)
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export const setStyle = (element, values, overwrite) => {
|
|
175
|
-
if (!element) {
|
|
176
|
-
return
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
// element.setAttribute("style", value) -> Fail when violates CSP
|
|
180
|
-
const style = element.style
|
|
181
|
-
|
|
182
|
-
if (typeof values === "string") {
|
|
183
|
-
style.cssText = overwrite ? values : style.cssText + ";" + values
|
|
184
|
-
return
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (overwrite) {
|
|
188
|
-
style.cssText = ""
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
for (const key in values) {
|
|
192
|
-
if (Object.hasOwn(values, key)) {
|
|
193
|
-
style[key] = values[key].replace("!important", "")
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
// convert `font-size: 12px; color: red` to `{"fontSize": "12px"; "color": "red"}`
|
|
199
|
-
// eslint-disable-next-line no-unused-vars
|
|
200
|
-
const toStyleKeyValues = (styleText) => {
|
|
201
|
-
const result = {}
|
|
202
|
-
const keyValues = styleText.split(/\s*;\s*/)
|
|
203
|
-
for (const keyValue of keyValues) {
|
|
204
|
-
const kv = keyValue.split(/\s*:\s*/)
|
|
205
|
-
// TODO: fix when key is such as -webkit-xxx
|
|
206
|
-
const key = toCamelCase(kv[0])
|
|
207
|
-
if (key) {
|
|
208
|
-
result[key] = kv[1]
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return result
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
export const toStyleMap = (styleText) => {
|
|
216
|
-
styleText = noStyleSpace(styleText)
|
|
217
|
-
const map = {}
|
|
218
|
-
const keyValues = styleText.split("}")
|
|
219
|
-
for (const keyValue of keyValues) {
|
|
220
|
-
const kv = keyValue.split("{")
|
|
221
|
-
if (kv[0] && kv[1]) {
|
|
222
|
-
map[kv[0]] = kv[1]
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
return map
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export const noStyleSpace = (text) =>
|
|
230
|
-
text.replaceAll(/\s*([^\w-+%!])\s*/gm, "$1")
|
|
231
|
-
|
|
232
|
-
export const createSetStyle = (styleText) => {
|
|
233
|
-
const styleMap = toStyleMap(styleText)
|
|
234
|
-
return (element, value, overwrite) => {
|
|
235
|
-
if (typeof value === "object") {
|
|
236
|
-
setStyle(element, value, overwrite)
|
|
237
|
-
} else if (typeof value === "string") {
|
|
238
|
-
const key = noStyleSpace(value)
|
|
239
|
-
const value2 = styleMap[key]
|
|
240
|
-
setStyle(element, value2 || value, overwrite)
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
export const isUrl = (text) => /^https?:\/\//.test(text)
|
|
246
|
-
|
|
247
|
-
/**
|
|
248
|
-
*
|
|
249
|
-
* @param { function } func
|
|
250
|
-
* @param { number } interval
|
|
251
|
-
* @returns
|
|
252
|
-
*/
|
|
253
|
-
export const throttle = (func, interval) => {
|
|
254
|
-
let timeoutId = null
|
|
255
|
-
let next = false
|
|
256
|
-
const handler = (...args) => {
|
|
257
|
-
if (timeoutId) {
|
|
258
|
-
next = true
|
|
259
|
-
} else {
|
|
260
|
-
func.apply(this, args)
|
|
261
|
-
timeoutId = setTimeout(() => {
|
|
262
|
-
timeoutId = null
|
|
263
|
-
if (next) {
|
|
264
|
-
next = false
|
|
265
|
-
handler()
|
|
266
|
-
}
|
|
267
|
-
}, interval)
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
return handler
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
// Polyfill for Object.hasOwn()
|
|
275
|
-
if (typeof Object.hasOwn !== "function") {
|
|
276
|
-
Object.hasOwn = (instance, prop) =>
|
|
277
|
-
// eslint-disable-next-line prefer-object-has-own
|
|
278
|
-
Object.prototype.hasOwnProperty.call(instance, prop)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
export const registerMenuCommand = () => undefined
|
|
282
|
-
|
|
283
|
-
export const extendHistoryApi = () => {
|
|
284
|
-
// https://dirask.com/posts/JavaScript-on-location-changed-event-on-url-changed-event-DKeyZj
|
|
285
|
-
const pushState = history.pushState
|
|
286
|
-
const replaceState = history.replaceState
|
|
287
|
-
|
|
288
|
-
history.pushState = function () {
|
|
289
|
-
// eslint-disable-next-line prefer-rest-params
|
|
290
|
-
pushState.apply(history, arguments)
|
|
291
|
-
globalThis.dispatchEvent(new Event("pushstate"))
|
|
292
|
-
globalThis.dispatchEvent(new Event("locationchange"))
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
history.replaceState = function () {
|
|
296
|
-
// eslint-disable-next-line prefer-rest-params
|
|
297
|
-
replaceState.apply(history, arguments)
|
|
298
|
-
globalThis.dispatchEvent(new Event("replacestate"))
|
|
299
|
-
globalThis.dispatchEvent(new Event("locationchange"))
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
globalThis.addEventListener("popstate", function () {
|
|
303
|
-
globalThis.dispatchEvent(new Event("locationchange"))
|
|
304
|
-
})
|
|
305
|
-
|
|
306
|
-
// Usage example:
|
|
307
|
-
// window.addEventListener("locationchange", function () {
|
|
308
|
-
// console.log("onlocationchange event occurred!")
|
|
309
|
-
// })
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
// eslint-disable-next-line no-script-url
|
|
313
|
-
export const actionHref = "javascript:;"
|
|
314
|
-
|
|
315
|
-
export const getOffsetPosition = (element, referElement) => {
|
|
316
|
-
const position = { top: 0, left: 0 }
|
|
317
|
-
referElement = referElement || doc.body
|
|
318
|
-
|
|
319
|
-
while (element && element !== referElement) {
|
|
320
|
-
position.top += element.offsetTop
|
|
321
|
-
position.left += element.offsetLeft
|
|
322
|
-
element = element.offsetParent
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
return position
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
const runOnceCache = {}
|
|
329
|
-
export const runOnce = async (key, func) => {
|
|
330
|
-
if (Object.hasOwn(runOnceCache, key)) {
|
|
331
|
-
return runOnceCache[key]
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
const result = await func()
|
|
335
|
-
|
|
336
|
-
if (key) {
|
|
337
|
-
runOnceCache[key] = result
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
return result
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
const cacheStore = {}
|
|
344
|
-
const makeKey = (key /* string | any[] */) =>
|
|
345
|
-
Array.isArray(key) ? key.join(":") : key
|
|
346
|
-
export const cache = {
|
|
347
|
-
get: (key /* string | any[] */) => cacheStore[makeKey(key)],
|
|
348
|
-
add(key /* string | any[] */, value /* any */) {
|
|
349
|
-
cacheStore[makeKey(key)] = value
|
|
350
|
-
},
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
export const sleep = async (time) => {
|
|
354
|
-
return new Promise((resolve) => {
|
|
355
|
-
setTimeout(() => {
|
|
356
|
-
resolve(1)
|
|
357
|
-
}, time)
|
|
358
|
-
})
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
export const parseInt10 = (number, defaultValue) => {
|
|
362
|
-
if (typeof number === "number" && !Number.isNaN(number)) {
|
|
363
|
-
return number
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (typeof defaultValue !== "number") {
|
|
367
|
-
defaultValue = Number.NaN
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
if (!number) {
|
|
371
|
-
return defaultValue
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
const result = Number.parseInt(number, 10)
|
|
375
|
-
return Number.isNaN(result) ? defaultValue : result
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
const rootFuncArray = []
|
|
379
|
-
const headFuncArray = []
|
|
380
|
-
const bodyFuncArray = []
|
|
381
|
-
let headBodyObserver
|
|
382
|
-
|
|
383
|
-
const startObserveHeadBodyExists = () => {
|
|
384
|
-
if (headBodyObserver) {
|
|
385
|
-
return
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
headBodyObserver = new MutationObserver(() => {
|
|
389
|
-
if (doc.head && doc.body) {
|
|
390
|
-
headBodyObserver.disconnect()
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
if (doc.documentElement && rootFuncArray.length > 0) {
|
|
394
|
-
for (const func of rootFuncArray) {
|
|
395
|
-
func()
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
rootFuncArray.length = 0
|
|
399
|
-
}
|
|
400
|
-
|
|
401
|
-
if (doc.head && headFuncArray.length > 0) {
|
|
402
|
-
for (const func of headFuncArray) {
|
|
403
|
-
func()
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
headFuncArray.length = 0
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
if (doc.body && bodyFuncArray.length > 0) {
|
|
410
|
-
for (const func of bodyFuncArray) {
|
|
411
|
-
func()
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
bodyFuncArray.length = 0
|
|
415
|
-
}
|
|
416
|
-
})
|
|
417
|
-
|
|
418
|
-
headBodyObserver.observe(doc, {
|
|
419
|
-
childList: true,
|
|
420
|
-
subtree: true,
|
|
421
|
-
})
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Run function when document.documentElement exsits.
|
|
426
|
-
*/
|
|
427
|
-
export const runWhenRootExists = (func) => {
|
|
428
|
-
if (!doc.documentElement) {
|
|
429
|
-
rootFuncArray.push(func)
|
|
430
|
-
startObserveHeadBodyExists()
|
|
431
|
-
return
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
func()
|
|
435
|
-
}
|
|
436
|
-
|
|
437
|
-
/**
|
|
438
|
-
* Run function when document.head exsits.
|
|
439
|
-
*/
|
|
440
|
-
export const runWhenHeadExists = (func) => {
|
|
441
|
-
if (!doc.head) {
|
|
442
|
-
headFuncArray.push(func)
|
|
443
|
-
startObserveHeadBodyExists()
|
|
444
|
-
return
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
func()
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
/**
|
|
451
|
-
* Run function when document.body exsits. The function executed before DOMContentLoaded.
|
|
452
|
-
*/
|
|
453
|
-
export const runWhenBodyExists = (func) => {
|
|
454
|
-
if (!doc.body) {
|
|
455
|
-
bodyFuncArray.push(func)
|
|
456
|
-
startObserveHeadBodyExists()
|
|
457
|
-
return
|
|
458
|
-
}
|
|
459
|
-
|
|
460
|
-
func()
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
/**
|
|
464
|
-
* Equals to jQuery.domready
|
|
465
|
-
*/
|
|
466
|
-
export const runWhenDomReady = (func) => {
|
|
467
|
-
if (doc.readyState === "interactive" || doc.readyState === "complete") {
|
|
468
|
-
return func()
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
const handler = () => {
|
|
472
|
-
if (doc.readyState === "interactive" || doc.readyState === "complete") {
|
|
473
|
-
func()
|
|
474
|
-
removeEventListener(doc, "readystatechange", handler)
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
addEventListener(doc, "readystatechange", handler)
|
|
479
|
-
}
|
|
480
|
-
|
|
481
|
-
export const isVisible = (element) => {
|
|
482
|
-
if (typeof element.checkVisibility === "function") {
|
|
483
|
-
return element.checkVisibility()
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
return element.offsetParent !== null
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
export const isTouchScreen = () => "ontouchstart" in win
|
|
490
|
-
|
|
491
|
-
const escapeHTMLPolicy =
|
|
492
|
-
typeof trustedTypes !== "undefined" &&
|
|
493
|
-
typeof trustedTypes.createPolicy === "function"
|
|
494
|
-
? trustedTypes.createPolicy("beuEscapePolicy", {
|
|
495
|
-
createHTML: (string) => string,
|
|
496
|
-
})
|
|
497
|
-
: undefined
|
|
498
|
-
|
|
499
|
-
export const createHTML = (html) => {
|
|
500
|
-
return escapeHTMLPolicy ? escapeHTMLPolicy.createHTML(html) : html
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
/**
|
|
504
|
-
* Compare two semantic version strings
|
|
505
|
-
* @param {string} v1 - First version string (e.g., "1.2.0")
|
|
506
|
-
* @param {string} v2 - Second version string (e.g., "1.1.5")
|
|
507
|
-
* @returns {number} - Returns 1 if v1 > v2, -1 if v1 < v2, 0 if equal
|
|
508
|
-
* @throws {Error} - Throws error for invalid version strings
|
|
509
|
-
*/
|
|
510
|
-
export function compareVersions(v1, v2) {
|
|
511
|
-
// Input validation
|
|
512
|
-
if (typeof v1 !== "string" || typeof v2 !== "string") {
|
|
513
|
-
throw new TypeError("Version strings must be of type string")
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (!v1.trim() || !v2.trim()) {
|
|
517
|
-
throw new Error("Version strings cannot be empty")
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// Validate version format (basic semantic versioning)
|
|
521
|
-
const versionRegex = /^\d+(\.\d+)*$/
|
|
522
|
-
if (!versionRegex.test(v1) || !versionRegex.test(v2)) {
|
|
523
|
-
throw new Error(
|
|
524
|
-
"Invalid version format. Use semantic versioning (e.g., '1.2.3')"
|
|
525
|
-
)
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
const v1Parts = v1.split(".").map(Number)
|
|
529
|
-
const v2Parts = v2.split(".").map(Number)
|
|
530
|
-
const maxLength = Math.max(v1Parts.length, v2Parts.length)
|
|
531
|
-
|
|
532
|
-
for (let i = 0; i < maxLength; i++) {
|
|
533
|
-
const num1 = v1Parts[i] || 0 // Use logical OR for cleaner default assignment
|
|
534
|
-
const num2 = v2Parts[i] || 0
|
|
535
|
-
|
|
536
|
-
if (num1 !== num2) {
|
|
537
|
-
return num1 > num2 ? 1 : -1 // Simplified comparison
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
return 0 // Versions are equal
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Usage:
|
|
545
|
-
// console.log(compareVersions("1.2.0", "1.1.5")); // Output: 1
|
|
546
|
-
// console.log(compareVersions("1.0", "1.0.0")); // Output: 0
|
|
547
|
-
// console.log(compareVersions("2.0", "1.5.10")); // Output: 1
|
|
548
|
-
// console.log(compareVersions("1.0.0", "1.0.0.1")); // Output: -1
|
package/lib/userscript.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
getRootElement,
|
|
3
|
-
setAttributes,
|
|
4
|
-
addElement as _addElement,
|
|
5
|
-
} from "./index.js"
|
|
6
|
-
|
|
7
|
-
export * from "./index.js"
|
|
8
|
-
|
|
9
|
-
// eslint-disable-next-line no-unused-expressions, n/prefer-global/process
|
|
10
|
-
process.env.PLASMO_TAG === "dev" &&
|
|
11
|
-
(() => {
|
|
12
|
-
const functions = document.GMFunctions
|
|
13
|
-
if (typeof functions === "object") {
|
|
14
|
-
for (const key in functions) {
|
|
15
|
-
if (Object.hasOwn(functions, key)) {
|
|
16
|
-
window[key] = functions[key]
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
})()
|
|
21
|
-
|
|
22
|
-
/* eslint-disable new-cap, camelcase */
|
|
23
|
-
export const addElement =
|
|
24
|
-
typeof GM_addElement === "function"
|
|
25
|
-
? (parentNode, tagName, attributes) => {
|
|
26
|
-
if (typeof parentNode === "string") {
|
|
27
|
-
return addElement(null, parentNode, tagName)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
if (!tagName) {
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
if (!parentNode) {
|
|
35
|
-
parentNode = /^(script|link|style|meta)$/.test(tagName)
|
|
36
|
-
? getRootElement(1)
|
|
37
|
-
: getRootElement(2)
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (typeof tagName === "string") {
|
|
41
|
-
let attributes2
|
|
42
|
-
if (attributes) {
|
|
43
|
-
const entries1 = []
|
|
44
|
-
const entries2 = []
|
|
45
|
-
for (const entry of Object.entries(attributes)) {
|
|
46
|
-
if (/^(on\w+|innerHTML)$/.test(entry[0])) {
|
|
47
|
-
entries2.push(entry)
|
|
48
|
-
} else {
|
|
49
|
-
entries1.push(entry)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
attributes = Object.fromEntries(entries1)
|
|
54
|
-
attributes2 = Object.fromEntries(entries2)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const element = GM_addElement(null, tagName, attributes)
|
|
58
|
-
setAttributes(element, attributes2)
|
|
59
|
-
parentNode.append(element)
|
|
60
|
-
return element
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// tagName: HTMLElement
|
|
64
|
-
setAttributes(tagName, attributes)
|
|
65
|
-
parentNode.append(tagName)
|
|
66
|
-
return tagName
|
|
67
|
-
}
|
|
68
|
-
: _addElement
|
|
69
|
-
|
|
70
|
-
export const addStyle = (styleText) =>
|
|
71
|
-
addElement(null, "style", { textContent: styleText })
|
|
72
|
-
|
|
73
|
-
// Only register menu on top frame
|
|
74
|
-
export const registerMenuCommand = (name, callback, options) => {
|
|
75
|
-
if (globalThis !== top) {
|
|
76
|
-
return
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
if (typeof GM.registerMenuCommand !== "function") {
|
|
80
|
-
console.warn("Do not support GM.registerMenuCommand!")
|
|
81
|
-
return
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
return GM.registerMenuCommand(name, callback, options)
|
|
85
|
-
}
|
|
86
|
-
/* eslint-enable new-cap, camelcase */
|