@sv443-network/userutils 1.1.2 → 1.2.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @sv443-network/userutils
2
2
 
3
+ ## 1.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 84b37f1: Added utility type Stringifiable to describe a string or any value that can be converted to one
8
+ - 142c5e2: Added function insertValues() to insert values into a string with placeholders
9
+ - 16ce257: Added lightweight translation system
10
+
11
+ ## 1.1.3
12
+
13
+ ### Patch Changes
14
+
15
+ - ad17374: Add support for OpenUserJS
16
+
3
17
  ## 1.1.2
4
18
 
5
19
  ### Patch Changes
package/README.md CHANGED
@@ -14,7 +14,7 @@ If you like using this library, please consider [supporting the development ❤
14
14
  - [**Preamble**](#preamble)
15
15
  - [**License**](#license)
16
16
  - [**Features**](#features)
17
- - [DOM:](#dom)
17
+ - [**DOM:**](#dom)
18
18
  - [onSelector()](#onselector) - call a listener once a selector is found in the DOM
19
19
  - [initOnSelector()](#initonselector) - needs to be called once to be able to use `onSelector()`
20
20
  - [getSelectorMap()](#getselectormap) - returns all currently registered selectors, listeners and options
@@ -28,21 +28,29 @@ If you like using this library, please consider [supporting the development ❤
28
28
  - [interceptWindowEvent()](#interceptwindowevent) - conditionally intercepts events registered by `addEventListener()` on the window object
29
29
  - [amplifyMedia()](#amplifymedia) - amplify an audio or video element's volume past the maximum of 100%
30
30
  - [isScrollable()](#isscrollable) - check if an element has a horizontal or vertical scroll bar
31
- - [Math:](#math)
31
+ - [**Math:**](#math)
32
32
  - [clamp()](#clamp) - constrain a number between a min and max value
33
33
  - [mapRange()](#maprange) - map a number from one range to the same spot in another range
34
34
  - [randRange()](#randrange) - generate a random number between a min and max boundary
35
- - [Misc:](#misc)
35
+ - [**Misc:**](#misc)
36
36
  - [ConfigManager()](#configmanager) - class that manages persistent userscript configurations, including data migration
37
37
  - [autoPlural()](#autoplural) - automatically pluralize a string
38
38
  - [pauseFor()](#pausefor) - pause the execution of a function for a given amount of time
39
39
  - [debounce()](#debounce) - call a function only once, after a given amount of time
40
40
  - [fetchAdvanced()](#fetchadvanced) - wrapper around the fetch API with a timeout option
41
- - [Arrays:](#arrays)
41
+ - [insertValues()](#insertvalues) - insert values into a string at specified placeholders
42
+ - [**Arrays:**](#arrays)
42
43
  - [randomItem()](#randomitem) - returns a random item from an array
43
44
  - [randomItemIndex()](#randomitemindex) - returns a tuple of a random item and its index from an array
44
45
  - [takeRandomItem()](#takerandomitem) - returns a random item from an array and mutates it to remove the item
45
46
  - [randomizeArray()](#randomizearray) - returns a copy of the array with its items in a random order
47
+ - [**Translation:**](#translation)
48
+ - [tr()](#tr) - simple translation of a string to another language
49
+ - [tr.addLanguage()](#traddlanguage) - add a language and its translations
50
+ - [tr.setLanguage()](#trsetlanguage) - set the currently active language for translations
51
+ - [tr.getLanguage()](#trgetlanguage) - returns the currently active language
52
+ - [**Utility types for TypeScript:**](#utility-types)
53
+ - [Stringifiable](#stringifiable) - any value that is a string or can be converted to one (implicitly or explicitly)
46
54
 
47
55
  <br><br>
48
56
 
@@ -131,7 +139,7 @@ This initialization function has to be called after `DOMContentLoaded` is fired
131
139
 
132
140
  Calling onSelector() before `DOMContentLoaded` is fired will not throw an error, but it also won't trigger listeners until the DOM is accessible.
133
141
 
134
- <details><summary><h4>Example - click to view</h4></summary>
142
+ <details><summary><b>Example - click to view</b></summary>
135
143
 
136
144
  ```ts
137
145
  import { initOnSelector, onSelector } from "@sv443-network/userutils";
@@ -178,7 +186,7 @@ You may see all options [here](https://developer.mozilla.org/en-US/docs/Web/API/
178
186
  >
179
187
  > ⚠️ Using these extra options can have a performance impact on larger sites or sites with a constantly changing DOM.
180
188
 
181
- <details><summary><h4>Example - click to view</h4></summary>
189
+ <details><summary><b>Example - click to view</b></summary>
182
190
 
183
191
  ```ts
184
192
  import { initOnSelector } from "@sv443-network/userutils";
@@ -204,7 +212,7 @@ getSelectorMap(): Map<string, OnSelectorOptions[]>
204
212
  Returns a Map of all currently registered selectors and their options, including listener function.
205
213
  Since multiple listeners can be registered for the same selector, the value of the Map is an array of `OnSelectorOptions` objects.
206
214
 
207
- <details><summary><h4>Example - click to view</h4></summary>
215
+ <details><summary><b>Example - click to view</b></summary>
208
216
 
209
217
  ```ts
210
218
  import { initOnSelector, onSelector, getSelectorMap } from "@sv443-network/userutils";
@@ -249,7 +257,7 @@ getUnsafeWindow(): Window
249
257
  Returns the unsafeWindow object or falls back to the regular window object if the `@grant unsafeWindow` is not given.
250
258
  Userscripts are sandboxed and do not have access to the regular window object, so this function is useful for websites that reject some events that were dispatched by the userscript.
251
259
 
252
- <details><summary><h4>Example - click to view</h4></summary>
260
+ <details><summary><b>Example - click to view</b></summary>
253
261
 
254
262
  ```ts
255
263
  import { getUnsafeWindow } from "@sv443-network/userutils";
@@ -281,7 +289,7 @@ The passed `afterElement` will be returned.
281
289
 
282
290
  ⚠️ This function needs to be run after the DOM has loaded (when using `@run-at document-end` or after `DOMContentLoaded` has fired).
283
291
 
284
- <details><summary><h4>Example - click to view</h4></summary>
292
+ <details><summary><b>Example - click to view</b></summary>
285
293
 
286
294
  ```ts
287
295
  import { insertAfter } from "@sv443-network/userutils";
@@ -309,7 +317,7 @@ Previously registered event listeners are kept intact.
309
317
 
310
318
  ⚠️ This function needs to be run after the DOM has loaded (when using `@run-at document-end` or after `DOMContentLoaded` has fired).
311
319
 
312
- <details><summary><h4>Example - click to view</h4></summary>
320
+ <details><summary><b>Example - click to view</b></summary>
313
321
 
314
322
  ```ts
315
323
  import { addParent } from "@sv443-network/userutils";
@@ -335,7 +343,7 @@ addGlobalStyle(css: string): void
335
343
  Adds a global style to the page in form of a `<style>` element that's inserted into the `<head>`.
336
344
  ⚠️ This function needs to be run after the DOM has loaded (when using `@run-at document-end` or after `DOMContentLoaded` has fired).
337
345
 
338
- <details><summary><h4>Example - click to view</h4></summary>
346
+ <details><summary><b>Example - click to view</b></summary>
339
347
 
340
348
  ```ts
341
349
  import { addGlobalStyle } from "@sv443-network/userutils";
@@ -363,7 +371,7 @@ Preloads images into browser cache by creating an invisible `<img>` element for
363
371
  The images will be loaded in parallel and the returned Promise will only resolve once all images have been loaded.
364
372
  The resulting PromiseSettledResult array will contain the image elements if resolved, or an ErrorEvent if rejected, but only if `rejects` is set to true.
365
373
 
366
- <details><summary><h4>Example - click to view</h4></summary>
374
+ <details><summary><b>Example - click to view</b></summary>
367
375
 
368
376
  ```ts
369
377
  import { preloadImages } from "@sv443-network/userutils";
@@ -394,7 +402,7 @@ This function has to be run in response to a user interaction event, else the br
394
402
 
395
403
  ⚠️ This function needs to be run after the DOM has loaded (when using `@run-at document-end` or after `DOMContentLoaded` has fired).
396
404
 
397
- <details><summary><h4>Example - click to view</h4></summary>
405
+ <details><summary><b>Example - click to view</b></summary>
398
406
 
399
407
  ```ts
400
408
  import { openInNewTab } from "@sv443-network/userutils";
@@ -423,7 +431,7 @@ Calling this function will set the `Error.stackTraceLimit` to 1000 (if it's not
423
431
 
424
432
  ⚠️ This function should be called as soon as possible (I recommend using `@run-at document-start`), as it will only intercept events that are *attached* after this function is called.
425
433
 
426
- <details><summary><h4>Example - click to view</h4></summary>
434
+ <details><summary><b>Example - click to view</b></summary>
427
435
 
428
436
  ```ts
429
437
  import { interceptEvent } from "@sv443-network/userutils";
@@ -453,7 +461,7 @@ This is essentially the same as [`interceptEvent()`](#interceptevent), but autom
453
461
  ⚠️ This function should be called as soon as possible (I recommend using `@run-at document-start`), as it will only intercept events that are *attached* after this function is called.
454
462
  ⚠️ In order for all events to be interceptable, the directive `@grant unsafeWindow` should be set.
455
463
 
456
- <details><summary><h4>Example - click to view</h4></summary>
464
+ <details><summary><b>Example - click to view</b></summary>
457
465
 
458
466
  ```ts
459
467
  import { interceptWindowEvent } from "@sv443-network/userutils";
@@ -489,7 +497,7 @@ The returned AmplifyMediaResult object has the following properties:
489
497
  | `source` | The MediaElementSourceNode instance |
490
498
  | `gain` | The GainNode instance |
491
499
 
492
- <details><summary><h4>Example - click to view</h4></summary>
500
+ <details><summary><b>Example - click to view</b></summary>
493
501
 
494
502
  ```ts
495
503
  import { amplifyMedia } from "@sv443-network/userutils";
@@ -527,7 +535,7 @@ isScrollable(element: Element): { horizontal: boolean, vertical: boolean }
527
535
  Checks if an element has a horizontal or vertical scroll bar.
528
536
  This uses the computed style of the element, so it will also work if the element is hidden.
529
537
 
530
- <details><summary><h4>Example - click to view</h4></summary>
538
+ <details><summary><b>Example - click to view</b></summary>
531
539
 
532
540
  ```ts
533
541
  import { isScrollable } from "@sv443-network/userutils";
@@ -553,7 +561,7 @@ clamp(num: number, min: number, max: number): number
553
561
 
554
562
  Clamps a number between a min and max boundary (inclusive).
555
563
 
556
- <details><summary><h4>Example - click to view</h4></summary>
564
+ <details><summary><b>Example - click to view</b></summary>
557
565
 
558
566
  ```ts
559
567
  import { clamp } from "@sv443-network/userutils";
@@ -586,7 +594,7 @@ mapRange(
586
594
 
587
595
  Maps a number from one range to the spot it would be in another range.
588
596
 
589
- <details><summary><h4>Example - click to view</h4></summary>
597
+ <details><summary><b>Example - click to view</b></summary>
590
598
 
591
599
  ```ts
592
600
  import { mapRange } from "@sv443-network/userutils";
@@ -613,7 +621,7 @@ randRange(max: number): number
613
621
  Returns a random number between `min` and `max` (inclusive).
614
622
  If only one argument is passed, it will be used as the `max` value and `min` will be set to 0.
615
623
 
616
- <details><summary><h4>Example - click to view</h4></summary>
624
+ <details><summary><b>Example - click to view</b></summary>
617
625
 
618
626
  ```ts
619
627
  import { randRange } from "@sv443-network/userutils";
@@ -676,7 +684,7 @@ If `loadData()` or `setData()` are called after this, the persistent storage wil
676
684
 
677
685
  <br>
678
686
 
679
- <details><summary><h4>Example - click to view</h4></summary>
687
+ <details><summary><b>Example - click to view</b></summary>
680
688
 
681
689
  ```ts
682
690
  import { ConfigManager } from "@sv443-network/userutils";
@@ -769,7 +777,7 @@ autoPlural(str: string, num: number | Array | NodeList): string
769
777
  Automatically pluralizes a string if the given number is not 1.
770
778
  If an array or NodeList is passed, the amount of contained items will be used.
771
779
 
772
- <details><summary><h4>Example - click to view</h4></summary>
780
+ <details><summary><b>Example - click to view</b></summary>
773
781
 
774
782
  ```ts
775
783
  import { autoPlural } from "@sv443-network/userutils";
@@ -797,7 +805,7 @@ pauseFor(ms: number): Promise<void>
797
805
 
798
806
  Pauses async execution for a given amount of time.
799
807
 
800
- <details><summary><h4>Example - click to view</h4></summary>
808
+ <details><summary><b>Example - click to view</b></summary>
801
809
 
802
810
  ```ts
803
811
  import { pauseFor } from "@sv443-network/userutils";
@@ -824,7 +832,7 @@ This is very useful for functions that are called repeatedly, like event listene
824
832
  All passed properties will be passed down to the debounced function.
825
833
  The timeout will default to 300ms if left undefined.
826
834
 
827
- <details><summary><h4>Example - click to view</h4></summary>
835
+ <details><summary><b>Example - click to view</b></summary>
828
836
 
829
837
  ```ts
830
838
  import { debounce } from "@sv443-network/userutils";
@@ -850,7 +858,7 @@ fetchAdvanced(url: string, options?: {
850
858
  A wrapper around the native `fetch()` function that adds options like a timeout property.
851
859
  The timeout will default to 10 seconds if left undefined.
852
860
 
853
- <details><summary><h4>Example - click to view</h4></summary>
861
+ <details><summary><b>Example - click to view</b></summary>
854
862
 
855
863
  ```ts
856
864
  import { fetchAdvanced } from "@sv443-network/userutils";
@@ -881,7 +889,7 @@ randomItem(array: Array): any
881
889
  Returns a random item from an array.
882
890
  Returns undefined if the array is empty.
883
891
 
884
- <details><summary><h4>Example - click to view</h4></summary>
892
+ <details><summary><b>Example - click to view</b></summary>
885
893
 
886
894
  ```ts
887
895
  import { randomItem } from "@sv443-network/userutils";
@@ -903,7 +911,7 @@ randomItemIndex(array: Array): [item: any, index: number]
903
911
  Returns a tuple of a random item and its index from an array.
904
912
  If the array is empty, it will return undefined for both values.
905
913
 
906
- <details><summary><h4>Example - click to view</h4></summary>
914
+ <details><summary><b>Example - click to view</b></summary>
907
915
 
908
916
  ```ts
909
917
  import { randomItemIndex } from "@sv443-network/userutils";
@@ -930,7 +938,7 @@ takeRandomItem(array: Array): any
930
938
  Returns a random item from an array and mutates the array by removing the item.
931
939
  Returns undefined if the array is empty.
932
940
 
933
- <details><summary><h4>Example - click to view</h4></summary>
941
+ <details><summary><b>Example - click to view</b></summary>
934
942
 
935
943
  ```ts
936
944
  import { takeRandomItem } from "@sv443-network/userutils";
@@ -953,7 +961,7 @@ randomizeArray(array: Array): Array
953
961
  Returns a copy of an array with its items in a random order.
954
962
  If the array is empty, the originally passed empty array will be returned without copying.
955
963
 
956
- <details><summary><h4>Example - click to view</h4></summary>
964
+ <details><summary><b>Example - click to view</b></summary>
957
965
 
958
966
  ```ts
959
967
  import { randomizeArray } from "@sv443-network/userutils";
@@ -968,8 +976,220 @@ console.log(foo); // [1, 2, 3, 4, 5, 6] - original array is not mutated
968
976
 
969
977
  </details>
970
978
 
979
+ <br><br>
980
+
981
+ ## Translation:
982
+ This is a very lightweight translation function that can be used to translate simple strings.
983
+ Pluralization is not supported but can be achieved manually by adding variations to the translations, identified by a different suffix. See the example section of [`tr.addLanguage()`](#traddlanguage) for an example on how this might be done.
984
+
985
+ <br>
986
+
987
+ ### tr()
988
+ Usage:
989
+ ```ts
990
+ tr(key: string, ...values: Stringifiable[]): string
991
+ ```
992
+
993
+ The function returns the translation of the passed key in the language added by [`tr.addLanguage()`](#traddlanguage) and set by [`tr.setLanguage()`](#trsetlanguage)
994
+ Should the translation contain placeholders in the format `%n`, where `n` is the number of the value starting at 1, they will be replaced with the respective item of the `values` rest parameter.
995
+ The items of the `values` rest parameter will be stringified using `toString()` (see [Stringifiable](#stringifiable)) before being inserted into the translation.
996
+
997
+ If the key is not found or no language has been added or set before calling this function, it will return the key itself.
998
+ If the key is found and the translation contains placeholders but no values are passed, it will return the translation as-is, including unmodified placeholders.
999
+ If the key is found, the translation doesn't contain placeholders but values are still passed, they will be ignored and the translation will be returned as-is.
1000
+
1001
+ <details><summary><b>Example - click to view</b></summary>
1002
+
1003
+ ```ts
1004
+ import { tr } from "@sv443-network/userutils";
1005
+
1006
+ tr.addLanguage("en", {
1007
+ "welcome": "Welcome",
1008
+ "welcome_name": "Welcome, %1",
1009
+ });
1010
+ tr.addLanguage("de", {
1011
+ "welcome": "Willkommen",
1012
+ "welcome_name": "Willkommen, %1",
1013
+ });
1014
+
1015
+ // this has to be called at least once before calling tr()
1016
+ tr.setLanguage("en");
1017
+
1018
+ console.log(tr("welcome")); // "Welcome"
1019
+ console.log(tr("welcome_name", "John")); // "Welcome, John"
1020
+ console.log(tr("non_existent_key")); // "non_existent_key"
1021
+
1022
+ // language can be changed at any time, synchronously
1023
+ tr.setLanguage("de");
1024
+
1025
+ console.log(tr("welcome")); // "Willkommen"
1026
+ console.log(tr("welcome_name", "John")); // "Willkommen, John"
1027
+ ```
1028
+
1029
+ </details>
1030
+
971
1031
  <br>
972
1032
 
1033
+ ### tr.addLanguage()
1034
+ Usage:
1035
+ ```ts
1036
+ tr.addLanguage(language: string, translations: Record<string, string>): void
1037
+ ```
1038
+
1039
+ Adds a language and its associated translations to the translation function.
1040
+ The passed language can be any unique identifier, though I recommend sticking to the [ISO 639-1 standard.](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)
1041
+ The passed translations should be an object where the key is the translation key used in `tr()` and the value is the translation itself.
1042
+ If `tr.addLanguage()` is called multiple times with the same language, the previous translations of that language will be overwritten.
1043
+
1044
+ The translation values may contain placeholders in the format `%n`, where `n` is the number of the value starting at 1.
1045
+ These can be used to inject values into the translation when calling `tr()`
1046
+
1047
+ <details><summary><b>Example - click to view</b></summary>
1048
+
1049
+ ```ts
1050
+ import { tr } from "@sv443-network/userutils";
1051
+
1052
+ // add a language with associated translations:
1053
+
1054
+ tr.addLanguage("de", {
1055
+ "color": "Farbe",
1056
+ });
1057
+
1058
+
1059
+ // with placeholders:
1060
+
1061
+ tr.addLanguage("en", {
1062
+ "welcome_generic": "Welcome!",
1063
+ "welcome_name": "Welcome, %1!",
1064
+ "welcome_extended": "Welcome, %1!\nYour last login was on %2\nYou have %3 unread messages",
1065
+ });
1066
+
1067
+
1068
+ // can be used for different locales too:
1069
+
1070
+ tr.addLanguage("en-US", {
1071
+ "fries": "french fries",
1072
+ });
1073
+ tr.addLanguage("en-GB", {
1074
+ "fries": "chips",
1075
+ });
1076
+
1077
+
1078
+ // apply default values for different locales to reduce redundancy in shared translation values:
1079
+
1080
+ const translation_de = {
1081
+ "greeting": "Guten Tag!",
1082
+ "foo": "Foo",
1083
+ };
1084
+ tr.addLanguage("de-DE", translation_de);
1085
+ tr.addLanguage("de-CH", {
1086
+ ...translation_de,
1087
+ // overwrite the "greeting" but keep other keys as they are
1088
+ "greeting": "Grüezi!",
1089
+ });
1090
+ tr.addLanguage("de-AT", {
1091
+ ...translation_de,
1092
+ // overwrite "greeting" again but keep other keys as they are
1093
+ "greeting": "Grüß Gott!",
1094
+ });
1095
+
1096
+
1097
+ // example for custom pluralization:
1098
+
1099
+ tr.addLanguage("en", {
1100
+ "items_added-0": "Added %1 items to your cart",
1101
+ "items_added-1": "Added %1 item to your cart",
1102
+ "items_added-n": "Added all %1 items to your cart",
1103
+ });
1104
+
1105
+ /** Returns the custom pluralization identifier for the given number of items (or size of Array/NodeList) */
1106
+ function pl(num: number | unknown[] | NodeList) {
1107
+ if(Array.isArray(num))
1108
+ num = num.length;
1109
+
1110
+ if(num === 0)
1111
+ return "0";
1112
+ else if(num === 1)
1113
+ return "1";
1114
+ else
1115
+ return "n";
1116
+ };
1117
+
1118
+ const items = [];
1119
+ tr(`items_added-${pl(items)}`, items.length); // "Added 0 items to your cart"
1120
+
1121
+ items.push("foo");
1122
+ tr(`items_added-${pl(items)}`, items.length); // "Added 1 item to your cart"
1123
+
1124
+ items.push("bar");
1125
+ tr(`items_added-${pl(items)}`, items.length); // "Added all 2 items to your cart"
1126
+ ```
1127
+
1128
+ </details>
1129
+
1130
+ <br>
1131
+
1132
+ ### tr.setLanguage()
1133
+ Usage:
1134
+ ```ts
1135
+ tr.setLanguage(language: string): void
1136
+ ```
1137
+
1138
+ Synchronously sets the language that will be used for translations.
1139
+ No validation is done on the passed language, so make sure it is correct and it has been added with `tr.addLanguage()` before calling `tr()`
1140
+
1141
+ For an example, see [`tr()`](#tr)
1142
+
1143
+ <br>
1144
+
1145
+ ### tr.getLanguage()
1146
+ Usage:
1147
+ ```ts
1148
+ tr.getLanguage(): string | undefined
1149
+ ```
1150
+
1151
+ Returns the currently active language set by [`tr.setLanguage()`](#trsetlanguage)
1152
+ If no language has been set yet, it will return undefined.
1153
+
1154
+ <br><br>
1155
+
1156
+ ## Utility types:
1157
+ UserUtils also offers some utility types that can be used in TypeScript projects.
1158
+ They don't alter the runtime behavior of the code, but they can be used to make the code more readable and to prevent errors.
1159
+
1160
+ ### Stringifiable
1161
+ This type describes any value that either is a string itself or can be converted to a string.
1162
+ To be considered stringifiable, the object needs to have a `toString()` method that returns a string (all primitive types have this method).
1163
+ This method allows not just explicit conversion by calling it, but also implicit conversion by passing it into the `String()` constructor or by interpolating it in a template string.
1164
+
1165
+ <details><summary><b>Example - click to view</b></summary>
1166
+
1167
+ ```ts
1168
+ import type { Stringifiable } from "@sv443-network/userutils";
1169
+
1170
+ function logSomething(value: Stringifiable) {
1171
+ console.log(`Log: ${value}`); // implicit conversion using `value.toString()`
1172
+ }
1173
+
1174
+ const fooObject = {
1175
+ toString: () => "hello world",
1176
+ };
1177
+
1178
+ const barObject = {
1179
+ baz: "",
1180
+ };
1181
+
1182
+ logSomething("foo"); // "Log: foo"
1183
+ logSomething(42); // "Log: 42"
1184
+ logSomething(true); // "Log: true"
1185
+ logSomething({}); // "Log: [object Object]"
1186
+ logSomething(Symbol(1)); // "Log: Symbol(1)"
1187
+ logSomething(fooObject); // "Log: hello world"
1188
+ logSomething(barObject); // type error
1189
+ ```
1190
+
1191
+ </details>
1192
+
973
1193
  <br><br><br><br>
974
1194
 
975
1195
  <div style="text-align: center;" align="center">
@@ -1,14 +1,24 @@
1
1
  // ==UserScript==
2
- // @name UserUtils
3
- // @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more
4
2
  // @namespace https://github.com/Sv443-Network/UserUtils
5
- // @version 1.1.2
6
- // @license MIT
3
+ // @exclude *
7
4
  // @author Sv443
8
- // @copyright Sv443 (https://github.com/Sv443)
9
5
  // @supportURL https://github.com/Sv443-Network/UserUtils/issues
10
6
  // @homepageURL https://github.com/Sv443-Network/UserUtils#readme
7
+ // @supportURL https://github.com/Sv443-Network/UserUtils/issues
8
+
9
+ // ==UserLibrary==
10
+ // @name UserUtils
11
+ // @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more
12
+ // @version 1.2.0
13
+ // @license MIT
14
+ // @copyright Sv443 (https://github.com/Sv443)
15
+
11
16
  // ==/UserScript==
17
+ // ==/UserLibrary==
18
+
19
+ // ==OpenUserJS==
20
+ // @author Sv443
21
+ // ==/OpenUserJS==
12
22
 
13
23
  var UserUtils = (function (exports) {
14
24
  var __defProp = Object.defineProperty;
@@ -360,6 +370,13 @@ var UserUtils = (function (exports) {
360
370
  return res;
361
371
  });
362
372
  }
373
+ function insertValues(str, ...values) {
374
+ return str.replace(/%\d/gm, (match) => {
375
+ var _a, _b;
376
+ const argIndex = Number(match.substring(1)) - 1;
377
+ return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? void 0 : _b.toString();
378
+ });
379
+ }
363
380
 
364
381
  // lib/onSelector.ts
365
382
  var selectorMap = /* @__PURE__ */ new Map();
@@ -411,6 +428,31 @@ var UserUtils = (function (exports) {
411
428
  return selectorMap;
412
429
  }
413
430
 
431
+ // lib/translation.ts
432
+ var trans = {};
433
+ var curLang;
434
+ function tr(key, ...args) {
435
+ var _a;
436
+ if (!curLang)
437
+ return key;
438
+ const trText = (_a = trans[curLang]) == null ? void 0 : _a[key];
439
+ if (!trText)
440
+ return key;
441
+ if (args.length > 0 && trText.match(/%\d/)) {
442
+ return insertValues(trText, ...args);
443
+ }
444
+ return trText;
445
+ }
446
+ tr.addLanguage = (language, translations) => {
447
+ trans[language] = translations;
448
+ };
449
+ tr.setLanguage = (language) => {
450
+ curLang = language;
451
+ };
452
+ tr.getLanguage = () => {
453
+ return curLang;
454
+ };
455
+
414
456
  exports.ConfigManager = ConfigManager;
415
457
  exports.addGlobalStyle = addGlobalStyle;
416
458
  exports.addParent = addParent;
@@ -423,6 +465,7 @@ var UserUtils = (function (exports) {
423
465
  exports.getUnsafeWindow = getUnsafeWindow;
424
466
  exports.initOnSelector = initOnSelector;
425
467
  exports.insertAfter = insertAfter;
468
+ exports.insertValues = insertValues;
426
469
  exports.interceptEvent = interceptEvent;
427
470
  exports.interceptWindowEvent = interceptWindowEvent;
428
471
  exports.isScrollable = isScrollable;
@@ -437,6 +480,7 @@ var UserUtils = (function (exports) {
437
480
  exports.randomizeArray = randomizeArray;
438
481
  exports.removeOnSelector = removeOnSelector;
439
482
  exports.takeRandomItem = takeRandomItem;
483
+ exports.tr = tr;
440
484
 
441
485
  return exports;
442
486
 
package/dist/index.js CHANGED
@@ -349,6 +349,13 @@ function fetchAdvanced(_0) {
349
349
  return res;
350
350
  });
351
351
  }
352
+ function insertValues(str, ...values) {
353
+ return str.replace(/%\d/gm, (match) => {
354
+ var _a, _b;
355
+ const argIndex = Number(match.substring(1)) - 1;
356
+ return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? void 0 : _b.toString();
357
+ });
358
+ }
352
359
 
353
360
  // lib/onSelector.ts
354
361
  var selectorMap = /* @__PURE__ */ new Map();
@@ -400,6 +407,31 @@ function getSelectorMap() {
400
407
  return selectorMap;
401
408
  }
402
409
 
410
+ // lib/translation.ts
411
+ var trans = {};
412
+ var curLang;
413
+ function tr(key, ...args) {
414
+ var _a;
415
+ if (!curLang)
416
+ return key;
417
+ const trText = (_a = trans[curLang]) == null ? void 0 : _a[key];
418
+ if (!trText)
419
+ return key;
420
+ if (args.length > 0 && trText.match(/%\d/)) {
421
+ return insertValues(trText, ...args);
422
+ }
423
+ return trText;
424
+ }
425
+ tr.addLanguage = (language, translations) => {
426
+ trans[language] = translations;
427
+ };
428
+ tr.setLanguage = (language) => {
429
+ curLang = language;
430
+ };
431
+ tr.getLanguage = () => {
432
+ return curLang;
433
+ };
434
+
403
435
  exports.ConfigManager = ConfigManager;
404
436
  exports.addGlobalStyle = addGlobalStyle;
405
437
  exports.addParent = addParent;
@@ -412,6 +444,7 @@ exports.getSelectorMap = getSelectorMap;
412
444
  exports.getUnsafeWindow = getUnsafeWindow;
413
445
  exports.initOnSelector = initOnSelector;
414
446
  exports.insertAfter = insertAfter;
447
+ exports.insertValues = insertValues;
415
448
  exports.interceptEvent = interceptEvent;
416
449
  exports.interceptWindowEvent = interceptWindowEvent;
417
450
  exports.isScrollable = isScrollable;
@@ -426,3 +459,4 @@ exports.randomItemIndex = randomItemIndex;
426
459
  exports.randomizeArray = randomizeArray;
427
460
  exports.removeOnSelector = removeOnSelector;
428
461
  exports.takeRandomItem = takeRandomItem;
462
+ exports.tr = tr;
package/dist/index.mjs CHANGED
@@ -347,6 +347,13 @@ function fetchAdvanced(_0) {
347
347
  return res;
348
348
  });
349
349
  }
350
+ function insertValues(str, ...values) {
351
+ return str.replace(/%\d/gm, (match) => {
352
+ var _a, _b;
353
+ const argIndex = Number(match.substring(1)) - 1;
354
+ return (_b = (_a = values[argIndex]) != null ? _a : match) == null ? void 0 : _b.toString();
355
+ });
356
+ }
350
357
 
351
358
  // lib/onSelector.ts
352
359
  var selectorMap = /* @__PURE__ */ new Map();
@@ -398,4 +405,29 @@ function getSelectorMap() {
398
405
  return selectorMap;
399
406
  }
400
407
 
401
- export { ConfigManager, addGlobalStyle, addParent, amplifyMedia, autoPlural, clamp, debounce, fetchAdvanced, getSelectorMap, getUnsafeWindow, initOnSelector, insertAfter, interceptEvent, interceptWindowEvent, isScrollable, mapRange, onSelector, openInNewTab, pauseFor, preloadImages, randRange, randomItem, randomItemIndex, randomizeArray, removeOnSelector, takeRandomItem };
408
+ // lib/translation.ts
409
+ var trans = {};
410
+ var curLang;
411
+ function tr(key, ...args) {
412
+ var _a;
413
+ if (!curLang)
414
+ return key;
415
+ const trText = (_a = trans[curLang]) == null ? void 0 : _a[key];
416
+ if (!trText)
417
+ return key;
418
+ if (args.length > 0 && trText.match(/%\d/)) {
419
+ return insertValues(trText, ...args);
420
+ }
421
+ return trText;
422
+ }
423
+ tr.addLanguage = (language, translations) => {
424
+ trans[language] = translations;
425
+ };
426
+ tr.setLanguage = (language) => {
427
+ curLang = language;
428
+ };
429
+ tr.getLanguage = () => {
430
+ return curLang;
431
+ };
432
+
433
+ export { ConfigManager, addGlobalStyle, addParent, amplifyMedia, autoPlural, clamp, debounce, fetchAdvanced, getSelectorMap, getUnsafeWindow, initOnSelector, insertAfter, insertValues, interceptEvent, interceptWindowEvent, isScrollable, mapRange, onSelector, openInNewTab, pauseFor, preloadImages, randRange, randomItem, randomItemIndex, randomizeArray, removeOnSelector, takeRandomItem, tr };
@@ -4,3 +4,4 @@ export * from "./dom";
4
4
  export * from "./math";
5
5
  export * from "./misc";
6
6
  export * from "./onSelector";
7
+ export * from "./translation";
@@ -1,9 +1,13 @@
1
+ /** Represents any value that is either a string itself or can be converted to one (implicitly or explicitly) because it has a toString() method */
2
+ export type Stringifiable = string | {
3
+ toString(): string;
4
+ };
1
5
  /**
2
6
  * Automatically appends an `s` to the passed `word`, if `num` is not equal to 1
3
7
  * @param word A word in singular form, to auto-convert to plural
4
8
  * @param num If this is an array or NodeList, the amount of items is used
5
9
  */
6
- export declare function autoPlural(word: string, num: number | unknown[] | NodeList): string;
10
+ export declare function autoPlural(word: Stringifiable, num: number | unknown[] | NodeList): string;
7
11
  /** Pauses async execution for the specified time in ms */
8
12
  export declare function pauseFor(time: number): Promise<void>;
9
13
  /**
@@ -18,3 +22,10 @@ export type FetchAdvancedOpts = RequestInit & Partial<{
18
22
  }>;
19
23
  /** Calls the fetch API with special options like a timeout */
20
24
  export declare function fetchAdvanced(url: string, options?: FetchAdvancedOpts): Promise<Response>;
25
+ /**
26
+ * Inserts the passed values into a string at the respective placeholders.
27
+ * The placeholder format is `%n`, where `n` is the 1-indexed argument number.
28
+ * @param str The string to insert the values into
29
+ * @param values The values to insert, in order, starting at `%1`
30
+ */
31
+ export declare function insertValues(str: string, ...values: Stringifiable[]): string;
@@ -0,0 +1,16 @@
1
+ import { Stringifiable } from "./misc";
2
+ /**
3
+ * Returns the translated text for the specified key in the current language set by `setLanguage()`
4
+ * If the key is not found in the previously registered translation, the key itself is returned.
5
+ *
6
+ * ⚠️ Remember to register a language with `tr.addLanguage()` and set it as active with `tr.setLanguage()` before using this function, otherwise it will always return the key itself.
7
+ * @param key Key of the translation to return
8
+ * @param args Optional arguments to be passed to the translated text. They will replace placeholders in the format `%n`, where `n` is the 1-indexed argument number
9
+ */
10
+ declare function tr(key: string, ...args: Stringifiable[]): string;
11
+ declare namespace tr {
12
+ var addLanguage: (language: string, translations: Record<string, string>) => void;
13
+ var setLanguage: (language: string) => void;
14
+ var getLanguage: () => string;
15
+ }
16
+ export { tr };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sv443-network/userutils",
3
- "version": "1.1.2",
3
+ "version": "1.2.0",
4
4
  "description": "Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, manage persistent user configurations, modify the DOM more easily and more",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",