@needle-tools/engine 4.11.3-next.2d2dff8 → 4.11.3-next.35d1b4d

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.
@@ -0,0 +1,48 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export declare namespace InternalAttributeUtils {
5
+ /**
6
+ * Checks if the given value is considered "falsey" in the context of HTML attributes.
7
+ * A value is considered falsey if it is "0" or "false" (case-insensitive).
8
+ *
9
+ * @param value - The attribute value to check.
10
+ * @returns True if the value is falsey, otherwise false.
11
+ */
12
+ function isFalsey(value: string | null): boolean;
13
+ /**
14
+ * Retrieves the value of the specified attribute from the given element.
15
+ * If the attribute value is considered falsey, it returns null.
16
+ * @returns The attribute value or null if falsey.
17
+ */
18
+ function getAttributeValueIfNotFalsey(element: Element, attributeName: string, opts?: {
19
+ onAttribute: (value: string) => void;
20
+ }): string | null;
21
+ /**
22
+ * Retrieves the value of the specified attribute from the given element.
23
+ * If the attribute value is considered falsey, it returns false.
24
+ * If the attribute is not set at all, it returns null.
25
+ * @returns The attribute value, false if falsey, or null if not set.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const result = HTMLAttributeUtils.getAttributeAndCheckFalsey(element, 'data-example', {
30
+ * onAttribute: (value, falsey) => {
31
+ * console.log(`Attribute value: ${value}
32
+ * , Is falsey: ${falsey}`);
33
+ * }
34
+ * });
35
+ *
36
+ * if (result === false) {
37
+ * console.log('The attribute is set to a falsey value.');
38
+ * } else if (result === null) {
39
+ * console.log('The attribute is not set.');
40
+ * } else {
41
+ * console.log(`The attribute value is: ${result}`);
42
+ * }
43
+ * ```
44
+ */
45
+ function getAttributeAndCheckFalsey(element: Element, attributeName: string, opts?: {
46
+ onAttribute: (value: string, falsey: boolean) => void;
47
+ }): false | string | null;
48
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * @internal
3
+ */
4
+ export var InternalAttributeUtils;
5
+ (function (InternalAttributeUtils) {
6
+ /**
7
+ * Checks if the given value is considered "falsey" in the context of HTML attributes.
8
+ * A value is considered falsey if it is "0" or "false" (case-insensitive).
9
+ *
10
+ * @param value - The attribute value to check.
11
+ * @returns True if the value is falsey, otherwise false.
12
+ */
13
+ function isFalsey(value) {
14
+ return value === "0" || value?.toLowerCase() === "false";
15
+ }
16
+ InternalAttributeUtils.isFalsey = isFalsey;
17
+ /**
18
+ * Retrieves the value of the specified attribute from the given element.
19
+ * If the attribute value is considered falsey, it returns null.
20
+ * @returns The attribute value or null if falsey.
21
+ */
22
+ function getAttributeValueIfNotFalsey(element, attributeName, opts) {
23
+ const attrValue = element.getAttribute(attributeName);
24
+ if (isFalsey(attrValue)) {
25
+ return null;
26
+ }
27
+ opts?.onAttribute?.call(null, attrValue);
28
+ return attrValue;
29
+ }
30
+ InternalAttributeUtils.getAttributeValueIfNotFalsey = getAttributeValueIfNotFalsey;
31
+ /**
32
+ * Retrieves the value of the specified attribute from the given element.
33
+ * If the attribute value is considered falsey, it returns false.
34
+ * If the attribute is not set at all, it returns null.
35
+ * @returns The attribute value, false if falsey, or null if not set.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const result = HTMLAttributeUtils.getAttributeAndCheckFalsey(element, 'data-example', {
40
+ * onAttribute: (value, falsey) => {
41
+ * console.log(`Attribute value: ${value}
42
+ * , Is falsey: ${falsey}`);
43
+ * }
44
+ * });
45
+ *
46
+ * if (result === false) {
47
+ * console.log('The attribute is set to a falsey value.');
48
+ * } else if (result === null) {
49
+ * console.log('The attribute is not set.');
50
+ * } else {
51
+ * console.log(`The attribute value is: ${result}`);
52
+ * }
53
+ * ```
54
+ */
55
+ function getAttributeAndCheckFalsey(element, attributeName, opts) {
56
+ const attrValue = element.getAttribute(attributeName);
57
+ // If the attribute is not set at all we just return
58
+ if (attrValue === null) {
59
+ return null;
60
+ }
61
+ if (isFalsey(attrValue)) {
62
+ opts?.onAttribute?.call(null, attrValue, true);
63
+ return false;
64
+ }
65
+ opts?.onAttribute?.call(null, attrValue, false);
66
+ return attrValue;
67
+ }
68
+ InternalAttributeUtils.getAttributeAndCheckFalsey = getAttributeAndCheckFalsey;
69
+ })(InternalAttributeUtils || (InternalAttributeUtils = {}));
70
+ //# sourceMappingURL=engine_utils_attributes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"engine_utils_attributes.js","sourceRoot":"","sources":["../../src/engine/engine_utils_attributes.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,MAAM,KAAW,sBAAsB,CAiEtC;AAjED,WAAiB,sBAAsB;IAEnC;;;;;;OAMG;IACH,SAAgB,QAAQ,CAAC,KAAoB;QACzC,OAAO,KAAK,KAAK,GAAG,IAAI,KAAK,EAAE,WAAW,EAAE,KAAK,OAAO,CAAC;IAC7D,CAAC;IAFe,+BAAQ,WAEvB,CAAA;IAED;;;;OAIG;IACH,SAAgB,4BAA4B,CAAC,OAAgB,EAAE,aAAqB,EAAE,IAA+C;QACjI,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QACtD,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE;YACrB,OAAO,IAAI,CAAC;SACf;QACD,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,SAAU,CAAC,CAAC;QAC1C,OAAO,SAAS,CAAC;IACrB,CAAC;IAPe,mDAA4B,+BAO3C,CAAA;IAED;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,SAAgB,0BAA0B,CAAC,OAAgB,EAAE,aAAqB,EAAE,IAA+D;QAC/I,MAAM,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,aAAa,CAAC,CAAC;QACtD,oDAAoD;QACpD,IAAG,SAAS,KAAK,IAAI,EAAE;YACnB,OAAO,IAAI,CAAC;SACf;QACD,IAAI,QAAQ,CAAC,SAAS,CAAC,EAAE;YACrB,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,SAAU,EAAE,IAAI,CAAC,CAAC;YAChD,OAAO,KAAK,CAAC;SAChB;QACD,IAAI,EAAE,WAAW,EAAE,IAAI,CAAC,IAAI,EAAE,SAAU,EAAE,KAAK,CAAC,CAAC;QACjD,OAAO,SAAS,CAAC;IACrB,CAAC;IAZe,iDAA0B,6BAYzC,CAAA;AAEL,CAAC,EAjEgB,sBAAsB,KAAtB,sBAAsB,QAiEtC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@needle-tools/engine",
3
- "version": "4.11.3-next.2d2dff8",
3
+ "version": "4.11.3-next.35d1b4d",
4
4
  "description": "Needle Engine is a web-based runtime for 3D apps. It runs on your machine for development with great integrations into editors like Unity or Blender - and can be deployed onto any device! It is flexible, extensible and networking and XR are built-in.",
5
5
  "main": "dist/needle-engine.min.js",
6
6
  "exports": {
@@ -4,9 +4,11 @@ import { Quaternion, Vector2, Vector3, Vector4 } from "three";
4
4
  declare type Vector = Vector2 | Vector3 | Vector4 | Quaternion;
5
5
 
6
6
  import { needleLogoOnlySVG } from "./assets/index.js";
7
+ import { isDevEnvironment } from "./debug/debug.js";
7
8
  import type { Context } from "./engine_context.js";
8
9
  import { ContextRegistry } from "./engine_context_registry.js";
9
10
  import { type SourceIdentifier } from "./engine_types.js";
11
+ import { InternalAttributeUtils } from "./engine_utils_attributes.js";
10
12
  import type { NeedleEngineWebComponent } from "./webcomponents/needle-engine.js";
11
13
 
12
14
  // https://schneidenbach.gitbooks.io/typescript-cookbook/content/nameof-operator.html
@@ -908,7 +910,7 @@ export async function PromiseAllWithErrors<T>(promise: Promise<T>[]): Promise<{
908
910
  * @param args.colorDark The color of the dark squares
909
911
  * @param args.colorLight The color of the light squares
910
912
  * @param args.correctLevel The error correction level to use
911
- * @param args.showLogo If true, the same logo as for the Needle loading screen will be drawn in the center of the QR code
913
+ * @param args.showLogo If true, the logo will be shown in the center of the QR code. By default the Needle Logo will be used. You can override which logo is being used by setting the `needle-engine` web component's `qr-logo-src` attribute. The logo can also be disabled by setting that attribute to a falsey value (e.g. "0" or "false")
912
914
  * @param args.showUrl If true, the URL will be shown below the QR code
913
915
  * @param args.domElement The dom element to append the QR code to. If not provided a new div will be created and returned
914
916
  * @returns The dom element containing the QR code
@@ -946,16 +948,16 @@ export async function generateQRCode(args: { domElement?: HTMLElement, text: str
946
948
  // Number of rows/columns of the generated QR code
947
949
  const moduleCount = qrCode?._oQRCode.moduleCount || 0;
948
950
  const canvas = qrCode?._oDrawing?._elCanvas as HTMLCanvasElement;
949
-
951
+
950
952
  let sizePercentage = 0.25;
951
953
  if (moduleCount < 40)
952
954
  sizePercentage = Math.floor(moduleCount / 4) / moduleCount;
953
955
  else
954
956
  sizePercentage = Math.floor(moduleCount / 6) / moduleCount;
955
-
957
+
956
958
  const paddingPercentage = Math.floor(moduleCount / 20) / moduleCount;
957
959
  try {
958
- const img = await addOverlays(canvas, { showLogo: args.showLogo, logoSize: sizePercentage, logoPadding: paddingPercentage }).catch(_e => { /** ignore */ });
960
+ const img = await internalRenderQRCodeOverlays(canvas, { showLogo: args.showLogo, logoSize: sizePercentage, logoPadding: paddingPercentage }).catch(_e => { /** ignore */ });
959
961
  if (img) {
960
962
  target.innerHTML = "";
961
963
  target.append(img);
@@ -1002,7 +1004,8 @@ export async function generateQRCode(args: { domElement?: HTMLElement, text: str
1002
1004
  return target;
1003
1005
  }
1004
1006
 
1005
- async function addOverlays(canvas: HTMLCanvasElement, args: { showLogo?: boolean, logoSize?: number, logoPadding?: number }): Promise<HTMLImageElement | void> {
1007
+
1008
+ async function internalRenderQRCodeOverlays(canvas: HTMLCanvasElement, args: { showLogo?: boolean, logoSize?: number, logoPadding?: number }): Promise<HTMLImageElement | void> {
1006
1009
  if (!canvas) return;
1007
1010
 
1008
1011
  // Internal settings
@@ -1019,18 +1022,38 @@ async function addOverlays(canvas: HTMLCanvasElement, args: { showLogo?: boolean
1019
1022
  const rectangleRadius = 0;
1020
1023
 
1021
1024
  // Draw the website's icon in the center of the QR code
1022
- const faviconImage = new Image();
1025
+ const image = new Image();
1023
1026
  const element = document.querySelector("needle-engine") as NeedleEngineWebComponent;
1024
- const logoSrc = element?.getAttribute("loading-logo-src") || needleLogoOnlySVG;
1027
+ if (!element) {
1028
+ console.debug("[QR Code] No web component found")
1029
+ }
1030
+
1031
+ // Query logo src from needle-engine attribute.
1032
+ // For any supported attribute it's possible to use "falsey" values (e.g. "0" or "false" to disable the logo in the QR code)
1033
+ let logoSrc: false | string | null = null;
1034
+ logoSrc = InternalAttributeUtils.getAttributeAndCheckFalsey(element, "qrcode-logo-src");
1035
+ if (args.showLogo !== true && logoSrc === false) return; // Explictly disabled
1036
+ logoSrc ||= InternalAttributeUtils.getAttributeAndCheckFalsey(element, "logo-src");
1037
+ if (args.showLogo !== true && logoSrc === false) return; // Explicitly disabled
1038
+ logoSrc ||= InternalAttributeUtils.getAttributeAndCheckFalsey(element, "loading-logo-src", {
1039
+ onAttribute: () => {
1040
+ if (isDevEnvironment()) console.warn("[QR Code] 'loading-logo-src' is deprecated, please use 'logo-src' or 'qrcode-logo-src' instead.");
1041
+ else console.debug("[QR Code] 'loading-logo-src' is deprecated.");
1042
+ }
1043
+ });
1044
+ if (args.showLogo !== true && logoSrc === false) return; // Explicitly disabled
1045
+
1046
+ logoSrc ||= needleLogoOnlySVG;
1025
1047
  if (!logoSrc) return;
1026
1048
 
1027
1049
  let haveLogo = false;
1028
1050
  if (args.showLogo !== false) {
1029
- faviconImage.src = logoSrc;
1051
+ image.src = logoSrc;
1030
1052
  haveLogo = await new Promise((resolve, _reject) => {
1031
- faviconImage.onload = () => resolve(true);
1032
- faviconImage.onerror = (err) => {
1033
- console.error("Error loading favicon image for QR code", err);
1053
+ image.onload = () => resolve(true);
1054
+ image.onerror = (err) => {
1055
+ let errorUrl = logoSrc !== needleLogoOnlySVG ? "'" + logoSrc + "'" : null;
1056
+ console.error("[QR Code] Error loading logo image for QR code", errorUrl, isDevEnvironment() ? err : "");
1034
1057
  resolve(false);
1035
1058
  };
1036
1059
  });
@@ -1073,7 +1096,7 @@ async function addOverlays(canvas: HTMLCanvasElement, args: { showLogo?: boolean
1073
1096
 
1074
1097
  if (haveLogo) {
1075
1098
  // Get aspect of image
1076
- const aspect = faviconImage.width / faviconImage.height;
1099
+ const aspect = image.width / image.height;
1077
1100
  if (aspect > 1) sizeY = sizeX / aspect;
1078
1101
  else sizeX = sizeY * aspect;
1079
1102
 
@@ -1114,7 +1137,7 @@ async function addOverlays(canvas: HTMLCanvasElement, args: { showLogo?: boolean
1114
1137
  paddedContext.shadowColor = "transparent";
1115
1138
  const logoX = (paddedCanvas.width - sizeX) / 2;
1116
1139
  const logoY = (paddedCanvas.height - sizeY) / 2;
1117
- paddedContext.drawImage(faviconImage, logoX, logoY, sizeX, sizeY);
1140
+ paddedContext.drawImage(image, logoX, logoY, sizeX, sizeY);
1118
1141
  }
1119
1142
 
1120
1143
  // Replace the canvas with the padded one
@@ -1125,4 +1148,5 @@ async function addOverlays(canvas: HTMLCanvasElement, args: { showLogo?: boolean
1125
1148
  img.style.height = "auto";
1126
1149
 
1127
1150
  return img;
1128
- }
1151
+ }
1152
+
@@ -0,0 +1,72 @@
1
+
2
+
3
+ /**
4
+ * @internal
5
+ */
6
+ export namespace InternalAttributeUtils {
7
+
8
+ /**
9
+ * Checks if the given value is considered "falsey" in the context of HTML attributes.
10
+ * A value is considered falsey if it is "0" or "false" (case-insensitive).
11
+ *
12
+ * @param value - The attribute value to check.
13
+ * @returns True if the value is falsey, otherwise false.
14
+ */
15
+ export function isFalsey(value: string | null): boolean {
16
+ return value === "0" || value?.toLowerCase() === "false";
17
+ }
18
+
19
+ /**
20
+ * Retrieves the value of the specified attribute from the given element.
21
+ * If the attribute value is considered falsey, it returns null.
22
+ * @returns The attribute value or null if falsey.
23
+ */
24
+ export function getAttributeValueIfNotFalsey(element: Element, attributeName: string, opts?: { onAttribute: (value: string) => void }): string | null {
25
+ const attrValue = element.getAttribute(attributeName);
26
+ if (isFalsey(attrValue)) {
27
+ return null;
28
+ }
29
+ opts?.onAttribute?.call(null, attrValue!);
30
+ return attrValue;
31
+ }
32
+
33
+ /**
34
+ * Retrieves the value of the specified attribute from the given element.
35
+ * If the attribute value is considered falsey, it returns false.
36
+ * If the attribute is not set at all, it returns null.
37
+ * @returns The attribute value, false if falsey, or null if not set.
38
+ *
39
+ * @example
40
+ * ```typescript
41
+ * const result = HTMLAttributeUtils.getAttributeAndCheckFalsey(element, 'data-example', {
42
+ * onAttribute: (value, falsey) => {
43
+ * console.log(`Attribute value: ${value}
44
+ * , Is falsey: ${falsey}`);
45
+ * }
46
+ * });
47
+ *
48
+ * if (result === false) {
49
+ * console.log('The attribute is set to a falsey value.');
50
+ * } else if (result === null) {
51
+ * console.log('The attribute is not set.');
52
+ * } else {
53
+ * console.log(`The attribute value is: ${result}`);
54
+ * }
55
+ * ```
56
+ */
57
+ export function getAttributeAndCheckFalsey(element: Element, attributeName: string, opts?: { onAttribute: (value: string, falsey:boolean) => void }): false | string | null {
58
+ const attrValue = element.getAttribute(attributeName);
59
+ // If the attribute is not set at all we just return
60
+ if(attrValue === null) {
61
+ return null;
62
+ }
63
+ if (isFalsey(attrValue)) {
64
+ opts?.onAttribute?.call(null, attrValue!, true);
65
+ return false;
66
+ }
67
+ opts?.onAttribute?.call(null, attrValue!, false);
68
+ return attrValue;
69
+ }
70
+
71
+ }
72
+