@sv443-network/userutils 6.2.0 → 6.3.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,12 @@
1
1
  # @sv443-network/userutils
2
2
 
3
+ ## 6.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - fa09004: Made `openInNewTab()` use `GM.openInTab` by default and fall back to the old behavior.
8
+ Also added `background` param to specify if the tab should get focus when opened.
9
+
3
10
  ## 6.2.0
4
11
 
5
12
  ### Minor Changes
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <!-- #MARKER Description -->
4
4
  ## UserUtils
5
- Zero-dependency 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
+ Zero-dependency library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and more.
6
6
 
7
7
  Contains builtin TypeScript declarations. Fully web compatible and supports ESM and CJS imports and global declaration.
8
8
  If you like using this library, please consider [supporting the development ❤️](https://github.com/sponsors/Sv443)
@@ -159,6 +159,7 @@ Additionally, there are the following extra options:
159
159
  - `disableOnNoListeners` - whether to disable the SelectorObserver when there are no listeners left (defaults to false)
160
160
  - `enableOnAddListener` - whether to enable the SelectorObserver when a new listener is added (defaults to true)
161
161
  - `defaultDebounce` - if set to a number, this debounce will be applied to every listener that doesn't have a custom debounce set (defaults to 0)
162
+ - `defaultDebounceEdge` - can be set to "falling" (default) or "rising", to call the function at (rising) on the very first call and subsequent times after the given debounce time or (falling) the very last call after the debounce time passed with no new calls - [see `debounce()` for more info and a diagram](#debounce)
162
163
 
163
164
  ⚠️ Make sure to call `enable()` to actually start observing. This will need to be done after the DOM has loaded (when using `@run-at document-end` or after `DOMContentLoaded` has fired) **and** as soon as the `baseElement` or `baseElementSelector` is available.
164
165
 
@@ -177,16 +178,19 @@ The listener will be called immediately if the selector already exists in the DO
177
178
  > This will also include elements that were already found in a previous listener call.
178
179
  > If set to false (default), querySelector() will be used and only the first matching element will be returned.
179
180
 
180
- > If `options.continuous` is set to true, the listener will not be deregistered after it was called once (defaults to false).
181
+ > If `options.continuous` is set to true, this listener will not be deregistered after it was called once (defaults to false).
181
182
  >
182
- > ⚠️ You should keep usage of this option to a minimum, as it will cause the listener to be called every time the selector is *checked for and found* and this can stack up quite quickly.
183
+ > ⚠️ You should keep usage of this option to a minimum, as it will cause this listener to be called every time the selector is *checked for and found* and this can stack up quite quickly.
183
184
  > ⚠️ You should try to only use this option on SelectorObserver instances that are scoped really low in the DOM tree to prevent as many selector checks as possible from being triggered.
184
185
  > ⚠️ I also recommend always setting a debounce time (see constructor or below) if you use this option.
185
186
 
186
- > If `options.debounce` is set to a number above 0, the listener will be debounced by that amount of milliseconds (defaults to 0).
187
- > E.g. if the debounce time is set to 200 and the selector is found twice within 100ms, only the last call of the listener will be executed.
187
+ > If `options.debounce` is set to a number above 0, this listener will be debounced by that amount of milliseconds (defaults to 0).
188
+ > E.g. if the debounce time is set to 200 and the selector is found twice within 100ms, only the last call of this listener will be executed.
189
+
190
+ > `options.debounceEdge` is set to "falling" by default, which means the debounce timer will start after the last call of this listener.
191
+ > If set to "rising", the debounce timer will start after the first call of this listener.
188
192
 
189
- > When using TypeScript, the generic `TElement` can be used to specify the type of the element(s) that the listener will return.
193
+ > When using TypeScript, the generic `TElement` can be used to specify the type of the element(s) that this listener will return.
190
194
  > It will default to HTMLElement if left undefined.
191
195
 
192
196
  <br>
@@ -262,6 +266,9 @@ document.addEventListener("DOMContentLoaded", () => {
262
266
  attributeFilter: ["class", "style", "data-whatever"],
263
267
  // debounce all listeners by 100ms unless specified otherwise:
264
268
  defaultDebounce: 100,
269
+ // "rising" means listeners are called immediately and use the debounce as a timeout between subsequent calls - see the debounce() function for a better explanation
270
+ defaultDebounceEdge: "rising",
271
+ // other settings from the MutationObserver API can be set here too - see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe#options
265
272
  });
266
273
 
267
274
  barObserver.addListener("#my-element", {
@@ -273,6 +280,8 @@ document.addEventListener("DOMContentLoaded", () => {
273
280
  barObserver.addListener("#my-other-element", {
274
281
  // set the debounce higher than provided by the defaultDebounce property:
275
282
  debounce: 250,
283
+ // adjust the debounceEdge back to the default "falling" for this specific listener:
284
+ debounceEdge: "falling",
276
285
  listener: (element) => {
277
286
  console.log("Other element's attributes changed:", element);
278
287
  },
@@ -573,14 +582,14 @@ preloadImages([
573
582
  ### openInNewTab()
574
583
  Usage:
575
584
  ```ts
576
- openInNewTab(url: string): void
585
+ openInNewTab(url: string, background?: boolean): void
577
586
  ```
578
587
 
579
- Creates an invisible anchor with a `_blank` target and clicks it.
580
- Contrary to `window.open()`, this has a lesser chance to get blocked by the browser's popup blocker and doesn't open the URL as a new window.
581
- This function has to be run in response to a user interaction event, else the browser might reject it.
588
+ Tries to use `GM.openInTab` to open the given URL in a new tab, or as a fallback if the grant is not given, creates an invisible anchor element and clicks it.
589
+ If `background` is set to true, the tab will be opened in the background. Leave `undefined` to use the browser's default behavior.
582
590
 
583
- ⚠️ This function needs to be run after the DOM has loaded (when using `@run-at document-end` or after `DOMContentLoaded` has fired).
591
+ ⚠️ Needs the `@grant GM.openInTab` directive, otherwise only the fallback behavior will be used and the warning below is extra important:
592
+ ⚠️ For the fallback to work, this function needs to be run in response to a user interaction event, else the browser might reject it.
584
593
 
585
594
  <details><summary><b>Example - click to view</b></summary>
586
595
 
@@ -588,7 +597,8 @@ This function has to be run in response to a user interaction event, else the br
588
597
  import { openInNewTab } from "@sv443-network/userutils";
589
598
 
590
599
  document.querySelector("#my-button").addEventListener("click", () => {
591
- openInNewTab("https://example.org/");
600
+ // open in background:
601
+ openInNewTab("https://example.org/", true);
592
602
  });
593
603
  ```
594
604
 
@@ -7,8 +7,8 @@
7
7
 
8
8
  // ==UserLibrary==
9
9
  // @name UserUtils
10
- // @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
11
- // @version 6.2.0
10
+ // @description Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and more
11
+ // @version 6.3.0
12
12
  // @license MIT
13
13
  // @copyright Sv443 (https://github.com/Sv443)
14
14
 
@@ -336,18 +336,22 @@ var UserUtils = (function (exports) {
336
336
  }));
337
337
  return Promise.allSettled(promises);
338
338
  }
339
- function openInNewTab(href) {
340
- const openElem = document.createElement("a");
341
- Object.assign(openElem, {
342
- className: "userutils-open-in-new-tab",
343
- target: "_blank",
344
- rel: "noopener noreferrer",
345
- href
346
- });
347
- openElem.style.display = "none";
348
- document.body.appendChild(openElem);
349
- openElem.click();
350
- setTimeout(openElem.remove, 50);
339
+ function openInNewTab(href, background) {
340
+ try {
341
+ GM.openInTab(href, background);
342
+ } catch (e) {
343
+ const openElem = document.createElement("a");
344
+ Object.assign(openElem, {
345
+ className: "userutils-open-in-new-tab",
346
+ target: "_blank",
347
+ rel: "noopener noreferrer",
348
+ href
349
+ });
350
+ openElem.style.display = "none";
351
+ document.body.appendChild(openElem);
352
+ openElem.click();
353
+ setTimeout(openElem.remove, 50);
354
+ }
351
355
  }
352
356
  function interceptEvent(eventObject, eventName, predicate = () => true) {
353
357
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
package/dist/index.js CHANGED
@@ -316,18 +316,22 @@ function preloadImages(srcUrls, rejects = false) {
316
316
  }));
317
317
  return Promise.allSettled(promises);
318
318
  }
319
- function openInNewTab(href) {
320
- const openElem = document.createElement("a");
321
- Object.assign(openElem, {
322
- className: "userutils-open-in-new-tab",
323
- target: "_blank",
324
- rel: "noopener noreferrer",
325
- href
326
- });
327
- openElem.style.display = "none";
328
- document.body.appendChild(openElem);
329
- openElem.click();
330
- setTimeout(openElem.remove, 50);
319
+ function openInNewTab(href, background) {
320
+ try {
321
+ GM.openInTab(href, background);
322
+ } catch (e) {
323
+ const openElem = document.createElement("a");
324
+ Object.assign(openElem, {
325
+ className: "userutils-open-in-new-tab",
326
+ target: "_blank",
327
+ rel: "noopener noreferrer",
328
+ href
329
+ });
330
+ openElem.style.display = "none";
331
+ document.body.appendChild(openElem);
332
+ openElem.click();
333
+ setTimeout(openElem.remove, 50);
334
+ }
331
335
  }
332
336
  function interceptEvent(eventObject, eventName, predicate = () => true) {
333
337
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
package/dist/index.mjs CHANGED
@@ -314,18 +314,22 @@ function preloadImages(srcUrls, rejects = false) {
314
314
  }));
315
315
  return Promise.allSettled(promises);
316
316
  }
317
- function openInNewTab(href) {
318
- const openElem = document.createElement("a");
319
- Object.assign(openElem, {
320
- className: "userutils-open-in-new-tab",
321
- target: "_blank",
322
- rel: "noopener noreferrer",
323
- href
324
- });
325
- openElem.style.display = "none";
326
- document.body.appendChild(openElem);
327
- openElem.click();
328
- setTimeout(openElem.remove, 50);
317
+ function openInNewTab(href, background) {
318
+ try {
319
+ GM.openInTab(href, background);
320
+ } catch (e) {
321
+ const openElem = document.createElement("a");
322
+ Object.assign(openElem, {
323
+ className: "userutils-open-in-new-tab",
324
+ target: "_blank",
325
+ rel: "noopener noreferrer",
326
+ href
327
+ });
328
+ openElem.style.display = "none";
329
+ document.body.appendChild(openElem);
330
+ openElem.click();
331
+ setTimeout(openElem.remove, 50);
332
+ }
329
333
  }
330
334
  function interceptEvent(eventObject, eventName, predicate = () => true) {
331
335
  Error.stackTraceLimit = Math.max(Error.stackTraceLimit, 100);
package/dist/lib/dom.d.ts CHANGED
@@ -26,12 +26,12 @@ export declare function addGlobalStyle(style: string): HTMLStyleElement;
26
26
  */
27
27
  export declare function preloadImages(srcUrls: string[], rejects?: boolean): Promise<PromiseSettledResult<unknown>[]>;
28
28
  /**
29
- * Creates an invisible anchor with a `_blank` target and clicks it.
30
- * Contrary to `window.open()`, this has a lesser chance to get blocked by the browser's popup blocker and doesn't open the URL as a new window.
31
- *
32
- * This function has to be run in response to a user interaction event, else the browser might reject it.
29
+ * Tries to use `GM.openInTab` to open the given URL in a new tab, otherwise if the grant is not given, creates an invisible anchor element and clicks it.
30
+ * For the fallback to work, this function needs to be run in response to a user interaction event, else the browser might reject it.
31
+ * @param href The URL to open in a new tab
32
+ * @param background If set to `true`, the tab will be opened in the background - set to `undefined` (default) to use the browser's default behavior
33
33
  */
34
- export declare function openInNewTab(href: string): void;
34
+ export declare function openInNewTab(href: string, background?: boolean): void;
35
35
  /**
36
36
  * Intercepts the specified event on the passed object and prevents it from being called if the called {@linkcode predicate} function returns a truthy value.
37
37
  * If no predicate is specified, all events will be discarded.
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@sv443-network/userutils",
3
3
  "libName": "UserUtils",
4
- "version": "6.2.0",
5
- "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
+ "version": "6.3.0",
5
+ "description": "Library with various utilities for userscripts - register listeners for when CSS selectors exist, intercept events, create persistent & synchronous data stores, modify the DOM more easily and more",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
8
8
  "types": "dist/lib/index.d.ts",