canvas-globe 1.0.0-beta.2 → 1.0.0-beta.3
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 +9 -0
- package/codemeta.json +1 -1
- package/dist/canvas-globe.umd.js +189 -33
- package/package.json +1 -1
- package/src/geo-globe.js +183 -30
- package/src/index.js +1 -0
- package/src/license.js +5 -2
- package/src/version.js +1 -1
- package/types/index.d.ts +1 -0
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,15 @@ All notable changes to this package are documented here. The format follows
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [1.0.0-beta.3] - 2026-09-17
|
|
10
|
+
|
|
11
|
+
- Replaced the small production license badge with a professional in-canvas
|
|
12
|
+
license panel containing purchase, preview and existing-key actions.
|
|
13
|
+
- Added a persistent unlicensed watermark and purchase ribbon after the panel
|
|
14
|
+
is dismissed, while keeping the globe interactive.
|
|
15
|
+
- Simplified customer-facing runtime copy to refer to the license key without
|
|
16
|
+
exposing its technical prefix convention.
|
|
17
|
+
|
|
9
18
|
## [1.0.0-beta.2] - 2026-09-17
|
|
10
19
|
|
|
11
20
|
- Corrected the project-creator help and README so beta users install the
|
package/codemeta.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"identifier": "canvas-globe",
|
|
6
6
|
"description": "CanvasGlobe helps developers add interactive 3D globes and flat world maps to JavaScript and React apps with Canvas 2D, without WebGL, map tiles, API keys, or runtime network calls.",
|
|
7
7
|
"url": "https://canvasglobe.swiftools.com/",
|
|
8
|
-
"version": "1.0.0-beta.
|
|
8
|
+
"version": "1.0.0-beta.3",
|
|
9
9
|
"codeRepository": "https://github.com/Shree-hari/canvas-globe",
|
|
10
10
|
"issueTracker": "https://github.com/Shree-hari/canvas-globe/issues",
|
|
11
11
|
"downloadUrl": "https://www.npmjs.com/package/canvas-globe",
|
package/dist/canvas-globe.umd.js
CHANGED
|
@@ -1314,7 +1314,7 @@ function drawFitted(ctx, media, box) {
|
|
|
1314
1314
|
}
|
|
1315
1315
|
|
|
1316
1316
|
// Keep in sync with package.json. Release checks enforce this value.
|
|
1317
|
-
const CANVAS_GLOBE_VERSION = "1.0.0-beta.
|
|
1317
|
+
const CANVAS_GLOBE_VERSION = "1.0.0-beta.3";
|
|
1318
1318
|
|
|
1319
1319
|
/** Local license-key checks and production-use presentation helpers. */
|
|
1320
1320
|
|
|
@@ -1322,6 +1322,8 @@ const DEFAULT_LICENSE_KEY = "0000-0000-000-0000";
|
|
|
1322
1322
|
const LICENSE_KEY_PREFIX = "GLO";
|
|
1323
1323
|
const LICENSE_PAGE_URL =
|
|
1324
1324
|
"https://canvasglobe.swiftools.com/pricing?utm_source=canvas-globe&utm_medium=runtime-notice";
|
|
1325
|
+
const LICENSE_SETUP_URL =
|
|
1326
|
+
"https://canvasglobe.swiftools.com/license-key?utm_source=canvas-globe&utm_medium=runtime-notice";
|
|
1325
1327
|
|
|
1326
1328
|
// This remains false while the latest published line is GPLv3. The commercial
|
|
1327
1329
|
// release checklist requires an intentional switch after the agreement and
|
|
@@ -1397,8 +1399,9 @@ function getLicensePresentation(
|
|
|
1397
1399
|
runtime,
|
|
1398
1400
|
notice: {
|
|
1399
1401
|
text: "CanvasGlobe: Purchase a license",
|
|
1400
|
-
ariaLabel: "CanvasGlobe requires a license for production use.
|
|
1402
|
+
ariaLabel: "CanvasGlobe requires a commercial license for production use.",
|
|
1401
1403
|
url: LICENSE_PAGE_URL,
|
|
1404
|
+
setupUrl: LICENSE_SETUP_URL,
|
|
1402
1405
|
},
|
|
1403
1406
|
};
|
|
1404
1407
|
}
|
|
@@ -1421,7 +1424,7 @@ function reportLicenseStatus(value, mode = COMMERCIAL_LICENSE_MODE) {
|
|
|
1421
1424
|
: `canvas-globe: ${DEFAULT_LICENSE_KEY} license key is not valid for production use. For help, email globe@swiftools.com`,
|
|
1422
1425
|
);
|
|
1423
1426
|
} else if (mode && status.kind === "invalid") {
|
|
1424
|
-
console.error(`canvas-globe: enter the
|
|
1427
|
+
console.error(`canvas-globe: enter the license key supplied after purchase. ${LICENSE_PAGE_URL}`);
|
|
1425
1428
|
}
|
|
1426
1429
|
return status;
|
|
1427
1430
|
}
|
|
@@ -1562,7 +1565,8 @@ class GeoGlobe {
|
|
|
1562
1565
|
this._media = new Map();
|
|
1563
1566
|
this._markerMedia = new Map();
|
|
1564
1567
|
this._counterShown = null;
|
|
1565
|
-
this.
|
|
1568
|
+
this._licenseHits = [];
|
|
1569
|
+
this._licenseDismissed = false;
|
|
1566
1570
|
this._licenseHovered = false;
|
|
1567
1571
|
|
|
1568
1572
|
this._applyWorld();
|
|
@@ -1597,6 +1601,8 @@ class GeoGlobe {
|
|
|
1597
1601
|
Object.assign(this.o, patch);
|
|
1598
1602
|
if ("licenseKey" in patch) {
|
|
1599
1603
|
reportLicenseStatus(patch.licenseKey);
|
|
1604
|
+
this._licenseDismissed = false;
|
|
1605
|
+
this._licenseHits = [];
|
|
1600
1606
|
}
|
|
1601
1607
|
if ("world" in patch) this._applyWorld();
|
|
1602
1608
|
if ("markers" in patch) this._applyMarkers(patch.markers || []);
|
|
@@ -2171,7 +2177,7 @@ class GeoGlobe {
|
|
|
2171
2177
|
this._markerMedia.clear();
|
|
2172
2178
|
this._tip = null;
|
|
2173
2179
|
this._live = null;
|
|
2174
|
-
this.
|
|
2180
|
+
this._licenseHits = [];
|
|
2175
2181
|
return this;
|
|
2176
2182
|
}
|
|
2177
2183
|
|
|
@@ -2567,9 +2573,17 @@ class GeoGlobe {
|
|
|
2567
2573
|
return null;
|
|
2568
2574
|
}
|
|
2569
2575
|
|
|
2576
|
+
_licenseActionAt(x, y) {
|
|
2577
|
+
for (let i = this._licenseHits.length - 1; i >= 0; i--) {
|
|
2578
|
+
const hit = this._licenseHits[i];
|
|
2579
|
+
if (x >= hit.x && x <= hit.x + hit.w && y >= hit.y && y <= hit.y + hit.h) return hit;
|
|
2580
|
+
}
|
|
2581
|
+
return null;
|
|
2582
|
+
}
|
|
2583
|
+
|
|
2570
2584
|
_licenseHitAt(x, y) {
|
|
2571
|
-
const hit = this.
|
|
2572
|
-
return Boolean(hit &&
|
|
2585
|
+
const hit = this._licenseActionAt(x, y);
|
|
2586
|
+
return Boolean(hit && hit.action !== "consume");
|
|
2573
2587
|
}
|
|
2574
2588
|
|
|
2575
2589
|
_bind() {
|
|
@@ -2577,6 +2591,12 @@ class GeoGlobe {
|
|
|
2577
2591
|
|
|
2578
2592
|
this._onDown = (e) => {
|
|
2579
2593
|
if (!this.o.interactive) return;
|
|
2594
|
+
const [localX, localY] = this._local(e);
|
|
2595
|
+
if (this._licenseActionAt(localX, localY)) {
|
|
2596
|
+
this._drag = null;
|
|
2597
|
+
this._dragMoved = false;
|
|
2598
|
+
return;
|
|
2599
|
+
}
|
|
2580
2600
|
this._pointers.set(e.pointerId, this._local(e));
|
|
2581
2601
|
c.setPointerCapture?.(e.pointerId);
|
|
2582
2602
|
if (this._pointers.size === 2) {
|
|
@@ -2680,9 +2700,17 @@ class GeoGlobe {
|
|
|
2680
2700
|
this._onClick = (e) => {
|
|
2681
2701
|
if (this._dragMoved) return;
|
|
2682
2702
|
const [x, y] = this._local(e);
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
if (
|
|
2703
|
+
const licenseAction = this._licenseActionAt(x, y);
|
|
2704
|
+
if (licenseAction) {
|
|
2705
|
+
if (licenseAction.action === "dismiss") {
|
|
2706
|
+
this._licenseDismissed = true;
|
|
2707
|
+
this._licenseHovered = false;
|
|
2708
|
+
this._dirty = true;
|
|
2709
|
+
this.render();
|
|
2710
|
+
} else if (licenseAction.url) {
|
|
2711
|
+
const opened = globalThis.open?.(licenseAction.url, "_blank", "noopener,noreferrer");
|
|
2712
|
+
if (opened) opened.opener = null;
|
|
2713
|
+
}
|
|
2686
2714
|
return;
|
|
2687
2715
|
}
|
|
2688
2716
|
const hit = this._hitAt(x, y);
|
|
@@ -3437,47 +3465,175 @@ class GeoGlobe {
|
|
|
3437
3465
|
ctx.restore();
|
|
3438
3466
|
}
|
|
3439
3467
|
|
|
3468
|
+
_licenseRect(ctx, x, y, width, height, radius) {
|
|
3469
|
+
ctx.beginPath();
|
|
3470
|
+
if (typeof ctx.roundRect === "function") ctx.roundRect(x, y, width, height, radius);
|
|
3471
|
+
else ctx.rect(x, y, width, height);
|
|
3472
|
+
}
|
|
3473
|
+
|
|
3440
3474
|
/** Licensing notice for public production use of the commercial edition. */
|
|
3441
3475
|
_paintLicenseNotice(w, h) {
|
|
3442
3476
|
const presentation = getLicensePresentation(this.o.licenseKey);
|
|
3443
3477
|
const notice = presentation.notice;
|
|
3444
|
-
this.
|
|
3478
|
+
this._licenseHits = [];
|
|
3445
3479
|
this.canvas.removeAttribute?.("data-canvas-globe-license-notice");
|
|
3480
|
+
this.canvas.removeAttribute?.("data-canvas-globe-license-view");
|
|
3481
|
+
this.canvas.removeAttribute?.("data-canvas-globe-license-state");
|
|
3446
3482
|
if (!notice) return;
|
|
3447
3483
|
|
|
3484
|
+
this.canvas.setAttribute?.("data-canvas-globe-license-notice", "visible");
|
|
3485
|
+
this.canvas.setAttribute?.("data-canvas-globe-license-state", presentation.status.kind);
|
|
3486
|
+
if (!this._licenseDismissed && w >= 240 && h >= 220) {
|
|
3487
|
+
this._paintLicenseDialog(w, h, notice);
|
|
3488
|
+
} else {
|
|
3489
|
+
this._paintLicenseWatermark(w, h, notice);
|
|
3490
|
+
}
|
|
3491
|
+
}
|
|
3492
|
+
|
|
3493
|
+
_paintLicenseDialog(w, h, notice) {
|
|
3448
3494
|
const { ctx } = this;
|
|
3449
|
-
const
|
|
3450
|
-
const
|
|
3451
|
-
const
|
|
3452
|
-
const
|
|
3495
|
+
const scale = Math.min(1, (w - 20) / 392, (h - 20) / 258);
|
|
3496
|
+
const width = 392 * scale;
|
|
3497
|
+
const height = 258 * scale;
|
|
3498
|
+
const x = (w - width) / 2;
|
|
3499
|
+
const y = (h - height) / 2;
|
|
3500
|
+
const headerHeight = 42 * scale;
|
|
3501
|
+
const inset = 24 * scale;
|
|
3502
|
+
const contentWidth = width - inset * 2;
|
|
3503
|
+
const radius = 10 * scale;
|
|
3453
3504
|
|
|
3454
3505
|
ctx.save();
|
|
3455
|
-
ctx.
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
ctx.
|
|
3463
|
-
ctx.fillStyle = "rgba(9, 18, 35, 0.92)";
|
|
3464
|
-
ctx.strokeStyle = "rgba(255, 255, 255, 0.3)";
|
|
3465
|
-
ctx.lineWidth = 1;
|
|
3506
|
+
ctx.fillStyle = "rgba(14, 19, 29, 0.98)";
|
|
3507
|
+
ctx.strokeStyle = "rgba(83, 98, 122, 0.92)";
|
|
3508
|
+
ctx.lineWidth = Math.max(1, scale);
|
|
3509
|
+
this._licenseRect(ctx, x, y, width, height, radius);
|
|
3510
|
+
ctx.fill();
|
|
3511
|
+
ctx.stroke();
|
|
3512
|
+
|
|
3513
|
+
ctx.strokeStyle = "rgba(63, 75, 96, 0.8)";
|
|
3466
3514
|
ctx.beginPath();
|
|
3467
|
-
|
|
3468
|
-
|
|
3515
|
+
ctx.moveTo(x, y + headerHeight);
|
|
3516
|
+
ctx.lineTo(x + width, y + headerHeight);
|
|
3517
|
+
ctx.stroke();
|
|
3518
|
+
|
|
3519
|
+
ctx.fillStyle = "#70d4ec";
|
|
3520
|
+
ctx.beginPath();
|
|
3521
|
+
ctx.arc(x + 18 * scale, y + headerHeight / 2, 3.5 * scale, 0, TAU);
|
|
3522
|
+
ctx.fill();
|
|
3523
|
+
|
|
3524
|
+
ctx.textBaseline = "middle";
|
|
3525
|
+
ctx.textAlign = "left";
|
|
3526
|
+
ctx.fillStyle = "#d5dbe5";
|
|
3527
|
+
ctx.font = `700 ${12 * scale}px Inter,system-ui,sans-serif`;
|
|
3528
|
+
ctx.fillText("CanvasGlobe", x + 29 * scale, y + headerHeight / 2);
|
|
3529
|
+
ctx.textAlign = "right";
|
|
3530
|
+
ctx.fillStyle = "#8994a8";
|
|
3531
|
+
ctx.font = `500 ${10.5 * scale}px Inter,system-ui,sans-serif`;
|
|
3532
|
+
ctx.fillText("License notice", x + width - 17 * scale, y + headerHeight / 2);
|
|
3533
|
+
|
|
3534
|
+
ctx.textAlign = "left";
|
|
3535
|
+
ctx.fillStyle = "#f2f4f8";
|
|
3536
|
+
ctx.font = `700 ${18 * scale}px Inter,system-ui,sans-serif`;
|
|
3537
|
+
ctx.fillText("Commercial use requires a license", x + inset, y + 76 * scale, contentWidth);
|
|
3538
|
+
ctx.fillStyle = "#aeb7c8";
|
|
3539
|
+
ctx.font = `400 ${12.5 * scale}px Inter,system-ui,sans-serif`;
|
|
3540
|
+
ctx.fillText("No valid license key was found for this installation.", x + inset, y + 102 * scale, contentWidth);
|
|
3541
|
+
|
|
3542
|
+
const primary = { x: x + inset, y: y + 117 * scale, w: contentWidth, h: 38 * scale };
|
|
3543
|
+
ctx.fillStyle = "#75d8ef";
|
|
3544
|
+
this._licenseRect(ctx, primary.x, primary.y, primary.w, primary.h, 7 * scale);
|
|
3545
|
+
ctx.fill();
|
|
3546
|
+
ctx.fillStyle = "#061118";
|
|
3547
|
+
ctx.textAlign = "center";
|
|
3548
|
+
ctx.font = `750 ${12.5 * scale}px Inter,system-ui,sans-serif`;
|
|
3549
|
+
ctx.fillText("Purchase license", primary.x + primary.w / 2, primary.y + primary.h / 2);
|
|
3550
|
+
|
|
3551
|
+
const dismiss = { x: x + inset, y: y + 163 * scale, w: contentWidth, h: 38 * scale };
|
|
3552
|
+
ctx.fillStyle = "rgba(0, 0, 0, 0)";
|
|
3553
|
+
ctx.strokeStyle = "#38445a";
|
|
3554
|
+
this._licenseRect(ctx, dismiss.x, dismiss.y, dismiss.w, dismiss.h, 7 * scale);
|
|
3469
3555
|
ctx.fill();
|
|
3470
3556
|
ctx.stroke();
|
|
3557
|
+
ctx.fillStyle = "#c1c8d5";
|
|
3558
|
+
ctx.fillText("Continue to preview", dismiss.x + dismiss.w / 2, dismiss.y + dismiss.h / 2);
|
|
3471
3559
|
|
|
3472
|
-
|
|
3473
|
-
ctx.fillStyle = "#
|
|
3560
|
+
const setup = { x: x + inset, y: y + 213 * scale, w: contentWidth, h: 25 * scale };
|
|
3561
|
+
ctx.fillStyle = "#8e99ad";
|
|
3562
|
+
ctx.font = `500 ${10.5 * scale}px Inter,system-ui,sans-serif`;
|
|
3563
|
+
ctx.fillText("Already purchased? Add your license key", setup.x + setup.w / 2, setup.y + setup.h / 2);
|
|
3564
|
+
ctx.restore();
|
|
3565
|
+
|
|
3566
|
+
this._licenseHits.push(
|
|
3567
|
+
{ action: "consume", x, y, w: width, h: height },
|
|
3568
|
+
{ action: "purchase", ...primary, url: notice.url },
|
|
3569
|
+
{ action: "dismiss", ...dismiss },
|
|
3570
|
+
{ action: "setup", ...setup, url: notice.setupUrl },
|
|
3571
|
+
);
|
|
3572
|
+
this.canvas.setAttribute?.("data-canvas-globe-license-view", "dialog");
|
|
3573
|
+
}
|
|
3574
|
+
|
|
3575
|
+
_paintLicenseWatermark(w, h, notice) {
|
|
3576
|
+
const { ctx } = this;
|
|
3577
|
+
const shortest = Math.min(w, h);
|
|
3578
|
+
const mainSize = clamp(shortest * 0.12, 27, 68);
|
|
3579
|
+
const ribbonMargin = clamp(shortest * 0.025, 8, 14);
|
|
3580
|
+
const ribbonHeight = clamp(h * 0.14, 42, 56);
|
|
3581
|
+
const ribbon = {
|
|
3582
|
+
x: ribbonMargin,
|
|
3583
|
+
y: h - ribbonHeight - ribbonMargin,
|
|
3584
|
+
w: w - ribbonMargin * 2,
|
|
3585
|
+
h: ribbonHeight,
|
|
3586
|
+
};
|
|
3587
|
+
|
|
3588
|
+
ctx.save();
|
|
3589
|
+
ctx.translate(w / 2, h * 0.47);
|
|
3590
|
+
ctx.rotate(-Math.PI / 15);
|
|
3474
3591
|
ctx.textAlign = "center";
|
|
3475
3592
|
ctx.textBaseline = "middle";
|
|
3476
|
-
ctx.
|
|
3593
|
+
ctx.fillStyle = "rgba(255, 255, 255, 0.28)";
|
|
3594
|
+
ctx.font = `900 ${mainSize}px Inter,system-ui,sans-serif`;
|
|
3595
|
+
ctx.fillText("UNLICENSED", 0, -mainSize * 0.18, w * 0.88);
|
|
3596
|
+
ctx.font = `800 ${mainSize * 0.4}px Inter,system-ui,sans-serif`;
|
|
3597
|
+
ctx.fillText("CANVASGLOBE", 0, mainSize * 0.55, w * 0.78);
|
|
3477
3598
|
ctx.restore();
|
|
3478
3599
|
|
|
3479
|
-
|
|
3480
|
-
|
|
3600
|
+
ctx.save();
|
|
3601
|
+
ctx.fillStyle = "rgba(12, 17, 28, 0.97)";
|
|
3602
|
+
ctx.strokeStyle = "rgba(73, 88, 113, 0.95)";
|
|
3603
|
+
ctx.lineWidth = 1;
|
|
3604
|
+
this._licenseRect(ctx, ribbon.x, ribbon.y, ribbon.w, ribbon.h, 9);
|
|
3605
|
+
ctx.fill();
|
|
3606
|
+
ctx.stroke();
|
|
3607
|
+
|
|
3608
|
+
const buttonWidth = clamp(ribbon.w * 0.29, 90, 136);
|
|
3609
|
+
const buttonHeight = Math.min(38, ribbon.h - 12);
|
|
3610
|
+
const purchase = {
|
|
3611
|
+
x: ribbon.x + ribbon.w - buttonWidth - 7,
|
|
3612
|
+
y: ribbon.y + (ribbon.h - buttonHeight) / 2,
|
|
3613
|
+
w: buttonWidth,
|
|
3614
|
+
h: buttonHeight,
|
|
3615
|
+
};
|
|
3616
|
+
ctx.fillStyle = "#75d8ef";
|
|
3617
|
+
this._licenseRect(ctx, purchase.x, purchase.y, purchase.w, purchase.h, 7);
|
|
3618
|
+
ctx.fill();
|
|
3619
|
+
ctx.textAlign = "center";
|
|
3620
|
+
ctx.textBaseline = "middle";
|
|
3621
|
+
ctx.fillStyle = "#061118";
|
|
3622
|
+
ctx.font = `750 ${clamp(buttonHeight * 0.32, 10, 12.5)}px Inter,system-ui,sans-serif`;
|
|
3623
|
+
ctx.fillText("Purchase license", purchase.x + purchase.w / 2, purchase.y + purchase.h / 2, purchase.w - 10);
|
|
3624
|
+
|
|
3625
|
+
ctx.textAlign = "left";
|
|
3626
|
+
ctx.fillStyle = "#d6dce6";
|
|
3627
|
+
ctx.font = `600 ${clamp(ribbonHeight * 0.24, 10, 13)}px Inter,system-ui,sans-serif`;
|
|
3628
|
+
const label = ribbon.w < 330 ? "License required" : "Unlicensed CanvasGlobe installation";
|
|
3629
|
+
ctx.fillText(label, ribbon.x + 14, ribbon.y + ribbon.h / 2, ribbon.w - buttonWidth - 35);
|
|
3630
|
+
ctx.restore();
|
|
3631
|
+
|
|
3632
|
+
this._licenseHits.push(
|
|
3633
|
+
{ action: "consume", ...ribbon },
|
|
3634
|
+
{ action: "purchase", ...purchase, url: notice.url },
|
|
3635
|
+
);
|
|
3636
|
+
this.canvas.setAttribute?.("data-canvas-globe-license-view", "watermark");
|
|
3481
3637
|
}
|
|
3482
3638
|
|
|
3483
3639
|
_paintCountries(t, trace, textured) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "canvas-globe",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.3",
|
|
4
4
|
"description": "Build interactive 3D globes and flat world maps in JavaScript or React with Canvas 2D, without WebGL, map tiles, API keys, or runtime network calls.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE.md",
|
|
6
6
|
"author": {
|
package/src/geo-globe.js
CHANGED
|
@@ -138,7 +138,8 @@ export class GeoGlobe {
|
|
|
138
138
|
this._media = new Map();
|
|
139
139
|
this._markerMedia = new Map();
|
|
140
140
|
this._counterShown = null;
|
|
141
|
-
this.
|
|
141
|
+
this._licenseHits = [];
|
|
142
|
+
this._licenseDismissed = false;
|
|
142
143
|
this._licenseHovered = false;
|
|
143
144
|
|
|
144
145
|
this._applyWorld();
|
|
@@ -173,6 +174,8 @@ export class GeoGlobe {
|
|
|
173
174
|
Object.assign(this.o, patch);
|
|
174
175
|
if ("licenseKey" in patch) {
|
|
175
176
|
reportLicenseStatus(patch.licenseKey);
|
|
177
|
+
this._licenseDismissed = false;
|
|
178
|
+
this._licenseHits = [];
|
|
176
179
|
}
|
|
177
180
|
if ("world" in patch) this._applyWorld();
|
|
178
181
|
if ("markers" in patch) this._applyMarkers(patch.markers || []);
|
|
@@ -747,7 +750,7 @@ export class GeoGlobe {
|
|
|
747
750
|
this._markerMedia.clear();
|
|
748
751
|
this._tip = null;
|
|
749
752
|
this._live = null;
|
|
750
|
-
this.
|
|
753
|
+
this._licenseHits = [];
|
|
751
754
|
return this;
|
|
752
755
|
}
|
|
753
756
|
|
|
@@ -1143,9 +1146,17 @@ export class GeoGlobe {
|
|
|
1143
1146
|
return null;
|
|
1144
1147
|
}
|
|
1145
1148
|
|
|
1149
|
+
_licenseActionAt(x, y) {
|
|
1150
|
+
for (let i = this._licenseHits.length - 1; i >= 0; i--) {
|
|
1151
|
+
const hit = this._licenseHits[i];
|
|
1152
|
+
if (x >= hit.x && x <= hit.x + hit.w && y >= hit.y && y <= hit.y + hit.h) return hit;
|
|
1153
|
+
}
|
|
1154
|
+
return null;
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1146
1157
|
_licenseHitAt(x, y) {
|
|
1147
|
-
const hit = this.
|
|
1148
|
-
return Boolean(hit &&
|
|
1158
|
+
const hit = this._licenseActionAt(x, y);
|
|
1159
|
+
return Boolean(hit && hit.action !== "consume");
|
|
1149
1160
|
}
|
|
1150
1161
|
|
|
1151
1162
|
_bind() {
|
|
@@ -1153,6 +1164,12 @@ export class GeoGlobe {
|
|
|
1153
1164
|
|
|
1154
1165
|
this._onDown = (e) => {
|
|
1155
1166
|
if (!this.o.interactive) return;
|
|
1167
|
+
const [localX, localY] = this._local(e);
|
|
1168
|
+
if (this._licenseActionAt(localX, localY)) {
|
|
1169
|
+
this._drag = null;
|
|
1170
|
+
this._dragMoved = false;
|
|
1171
|
+
return;
|
|
1172
|
+
}
|
|
1156
1173
|
this._pointers.set(e.pointerId, this._local(e));
|
|
1157
1174
|
c.setPointerCapture?.(e.pointerId);
|
|
1158
1175
|
if (this._pointers.size === 2) {
|
|
@@ -1256,9 +1273,17 @@ export class GeoGlobe {
|
|
|
1256
1273
|
this._onClick = (e) => {
|
|
1257
1274
|
if (this._dragMoved) return;
|
|
1258
1275
|
const [x, y] = this._local(e);
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
if (
|
|
1276
|
+
const licenseAction = this._licenseActionAt(x, y);
|
|
1277
|
+
if (licenseAction) {
|
|
1278
|
+
if (licenseAction.action === "dismiss") {
|
|
1279
|
+
this._licenseDismissed = true;
|
|
1280
|
+
this._licenseHovered = false;
|
|
1281
|
+
this._dirty = true;
|
|
1282
|
+
this.render();
|
|
1283
|
+
} else if (licenseAction.url) {
|
|
1284
|
+
const opened = globalThis.open?.(licenseAction.url, "_blank", "noopener,noreferrer");
|
|
1285
|
+
if (opened) opened.opener = null;
|
|
1286
|
+
}
|
|
1262
1287
|
return;
|
|
1263
1288
|
}
|
|
1264
1289
|
const hit = this._hitAt(x, y);
|
|
@@ -2013,47 +2038,175 @@ export class GeoGlobe {
|
|
|
2013
2038
|
ctx.restore();
|
|
2014
2039
|
}
|
|
2015
2040
|
|
|
2041
|
+
_licenseRect(ctx, x, y, width, height, radius) {
|
|
2042
|
+
ctx.beginPath();
|
|
2043
|
+
if (typeof ctx.roundRect === "function") ctx.roundRect(x, y, width, height, radius);
|
|
2044
|
+
else ctx.rect(x, y, width, height);
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2016
2047
|
/** Licensing notice for public production use of the commercial edition. */
|
|
2017
2048
|
_paintLicenseNotice(w, h) {
|
|
2018
2049
|
const presentation = getLicensePresentation(this.o.licenseKey);
|
|
2019
2050
|
const notice = presentation.notice;
|
|
2020
|
-
this.
|
|
2051
|
+
this._licenseHits = [];
|
|
2021
2052
|
this.canvas.removeAttribute?.("data-canvas-globe-license-notice");
|
|
2053
|
+
this.canvas.removeAttribute?.("data-canvas-globe-license-view");
|
|
2054
|
+
this.canvas.removeAttribute?.("data-canvas-globe-license-state");
|
|
2022
2055
|
if (!notice) return;
|
|
2023
2056
|
|
|
2057
|
+
this.canvas.setAttribute?.("data-canvas-globe-license-notice", "visible");
|
|
2058
|
+
this.canvas.setAttribute?.("data-canvas-globe-license-state", presentation.status.kind);
|
|
2059
|
+
if (!this._licenseDismissed && w >= 240 && h >= 220) {
|
|
2060
|
+
this._paintLicenseDialog(w, h, notice);
|
|
2061
|
+
} else {
|
|
2062
|
+
this._paintLicenseWatermark(w, h, notice);
|
|
2063
|
+
}
|
|
2064
|
+
}
|
|
2065
|
+
|
|
2066
|
+
_paintLicenseDialog(w, h, notice) {
|
|
2024
2067
|
const { ctx } = this;
|
|
2025
|
-
const
|
|
2026
|
-
const
|
|
2027
|
-
const
|
|
2028
|
-
const
|
|
2068
|
+
const scale = Math.min(1, (w - 20) / 392, (h - 20) / 258);
|
|
2069
|
+
const width = 392 * scale;
|
|
2070
|
+
const height = 258 * scale;
|
|
2071
|
+
const x = (w - width) / 2;
|
|
2072
|
+
const y = (h - height) / 2;
|
|
2073
|
+
const headerHeight = 42 * scale;
|
|
2074
|
+
const inset = 24 * scale;
|
|
2075
|
+
const contentWidth = width - inset * 2;
|
|
2076
|
+
const radius = 10 * scale;
|
|
2029
2077
|
|
|
2030
2078
|
ctx.save();
|
|
2031
|
-
ctx.
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
ctx.
|
|
2039
|
-
ctx.fillStyle = "rgba(9, 18, 35, 0.92)";
|
|
2040
|
-
ctx.strokeStyle = "rgba(255, 255, 255, 0.3)";
|
|
2041
|
-
ctx.lineWidth = 1;
|
|
2079
|
+
ctx.fillStyle = "rgba(14, 19, 29, 0.98)";
|
|
2080
|
+
ctx.strokeStyle = "rgba(83, 98, 122, 0.92)";
|
|
2081
|
+
ctx.lineWidth = Math.max(1, scale);
|
|
2082
|
+
this._licenseRect(ctx, x, y, width, height, radius);
|
|
2083
|
+
ctx.fill();
|
|
2084
|
+
ctx.stroke();
|
|
2085
|
+
|
|
2086
|
+
ctx.strokeStyle = "rgba(63, 75, 96, 0.8)";
|
|
2042
2087
|
ctx.beginPath();
|
|
2043
|
-
|
|
2044
|
-
|
|
2088
|
+
ctx.moveTo(x, y + headerHeight);
|
|
2089
|
+
ctx.lineTo(x + width, y + headerHeight);
|
|
2090
|
+
ctx.stroke();
|
|
2091
|
+
|
|
2092
|
+
ctx.fillStyle = "#70d4ec";
|
|
2093
|
+
ctx.beginPath();
|
|
2094
|
+
ctx.arc(x + 18 * scale, y + headerHeight / 2, 3.5 * scale, 0, TAU);
|
|
2095
|
+
ctx.fill();
|
|
2096
|
+
|
|
2097
|
+
ctx.textBaseline = "middle";
|
|
2098
|
+
ctx.textAlign = "left";
|
|
2099
|
+
ctx.fillStyle = "#d5dbe5";
|
|
2100
|
+
ctx.font = `700 ${12 * scale}px Inter,system-ui,sans-serif`;
|
|
2101
|
+
ctx.fillText("CanvasGlobe", x + 29 * scale, y + headerHeight / 2);
|
|
2102
|
+
ctx.textAlign = "right";
|
|
2103
|
+
ctx.fillStyle = "#8994a8";
|
|
2104
|
+
ctx.font = `500 ${10.5 * scale}px Inter,system-ui,sans-serif`;
|
|
2105
|
+
ctx.fillText("License notice", x + width - 17 * scale, y + headerHeight / 2);
|
|
2106
|
+
|
|
2107
|
+
ctx.textAlign = "left";
|
|
2108
|
+
ctx.fillStyle = "#f2f4f8";
|
|
2109
|
+
ctx.font = `700 ${18 * scale}px Inter,system-ui,sans-serif`;
|
|
2110
|
+
ctx.fillText("Commercial use requires a license", x + inset, y + 76 * scale, contentWidth);
|
|
2111
|
+
ctx.fillStyle = "#aeb7c8";
|
|
2112
|
+
ctx.font = `400 ${12.5 * scale}px Inter,system-ui,sans-serif`;
|
|
2113
|
+
ctx.fillText("No valid license key was found for this installation.", x + inset, y + 102 * scale, contentWidth);
|
|
2114
|
+
|
|
2115
|
+
const primary = { x: x + inset, y: y + 117 * scale, w: contentWidth, h: 38 * scale };
|
|
2116
|
+
ctx.fillStyle = "#75d8ef";
|
|
2117
|
+
this._licenseRect(ctx, primary.x, primary.y, primary.w, primary.h, 7 * scale);
|
|
2118
|
+
ctx.fill();
|
|
2119
|
+
ctx.fillStyle = "#061118";
|
|
2120
|
+
ctx.textAlign = "center";
|
|
2121
|
+
ctx.font = `750 ${12.5 * scale}px Inter,system-ui,sans-serif`;
|
|
2122
|
+
ctx.fillText("Purchase license", primary.x + primary.w / 2, primary.y + primary.h / 2);
|
|
2123
|
+
|
|
2124
|
+
const dismiss = { x: x + inset, y: y + 163 * scale, w: contentWidth, h: 38 * scale };
|
|
2125
|
+
ctx.fillStyle = "rgba(0, 0, 0, 0)";
|
|
2126
|
+
ctx.strokeStyle = "#38445a";
|
|
2127
|
+
this._licenseRect(ctx, dismiss.x, dismiss.y, dismiss.w, dismiss.h, 7 * scale);
|
|
2045
2128
|
ctx.fill();
|
|
2046
2129
|
ctx.stroke();
|
|
2130
|
+
ctx.fillStyle = "#c1c8d5";
|
|
2131
|
+
ctx.fillText("Continue to preview", dismiss.x + dismiss.w / 2, dismiss.y + dismiss.h / 2);
|
|
2047
2132
|
|
|
2048
|
-
|
|
2049
|
-
ctx.fillStyle = "#
|
|
2133
|
+
const setup = { x: x + inset, y: y + 213 * scale, w: contentWidth, h: 25 * scale };
|
|
2134
|
+
ctx.fillStyle = "#8e99ad";
|
|
2135
|
+
ctx.font = `500 ${10.5 * scale}px Inter,system-ui,sans-serif`;
|
|
2136
|
+
ctx.fillText("Already purchased? Add your license key", setup.x + setup.w / 2, setup.y + setup.h / 2);
|
|
2137
|
+
ctx.restore();
|
|
2138
|
+
|
|
2139
|
+
this._licenseHits.push(
|
|
2140
|
+
{ action: "consume", x, y, w: width, h: height },
|
|
2141
|
+
{ action: "purchase", ...primary, url: notice.url },
|
|
2142
|
+
{ action: "dismiss", ...dismiss },
|
|
2143
|
+
{ action: "setup", ...setup, url: notice.setupUrl },
|
|
2144
|
+
);
|
|
2145
|
+
this.canvas.setAttribute?.("data-canvas-globe-license-view", "dialog");
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
_paintLicenseWatermark(w, h, notice) {
|
|
2149
|
+
const { ctx } = this;
|
|
2150
|
+
const shortest = Math.min(w, h);
|
|
2151
|
+
const mainSize = clamp(shortest * 0.12, 27, 68);
|
|
2152
|
+
const ribbonMargin = clamp(shortest * 0.025, 8, 14);
|
|
2153
|
+
const ribbonHeight = clamp(h * 0.14, 42, 56);
|
|
2154
|
+
const ribbon = {
|
|
2155
|
+
x: ribbonMargin,
|
|
2156
|
+
y: h - ribbonHeight - ribbonMargin,
|
|
2157
|
+
w: w - ribbonMargin * 2,
|
|
2158
|
+
h: ribbonHeight,
|
|
2159
|
+
};
|
|
2160
|
+
|
|
2161
|
+
ctx.save();
|
|
2162
|
+
ctx.translate(w / 2, h * 0.47);
|
|
2163
|
+
ctx.rotate(-Math.PI / 15);
|
|
2050
2164
|
ctx.textAlign = "center";
|
|
2051
2165
|
ctx.textBaseline = "middle";
|
|
2052
|
-
ctx.
|
|
2166
|
+
ctx.fillStyle = "rgba(255, 255, 255, 0.28)";
|
|
2167
|
+
ctx.font = `900 ${mainSize}px Inter,system-ui,sans-serif`;
|
|
2168
|
+
ctx.fillText("UNLICENSED", 0, -mainSize * 0.18, w * 0.88);
|
|
2169
|
+
ctx.font = `800 ${mainSize * 0.4}px Inter,system-ui,sans-serif`;
|
|
2170
|
+
ctx.fillText("CANVASGLOBE", 0, mainSize * 0.55, w * 0.78);
|
|
2053
2171
|
ctx.restore();
|
|
2054
2172
|
|
|
2055
|
-
|
|
2056
|
-
|
|
2173
|
+
ctx.save();
|
|
2174
|
+
ctx.fillStyle = "rgba(12, 17, 28, 0.97)";
|
|
2175
|
+
ctx.strokeStyle = "rgba(73, 88, 113, 0.95)";
|
|
2176
|
+
ctx.lineWidth = 1;
|
|
2177
|
+
this._licenseRect(ctx, ribbon.x, ribbon.y, ribbon.w, ribbon.h, 9);
|
|
2178
|
+
ctx.fill();
|
|
2179
|
+
ctx.stroke();
|
|
2180
|
+
|
|
2181
|
+
const buttonWidth = clamp(ribbon.w * 0.29, 90, 136);
|
|
2182
|
+
const buttonHeight = Math.min(38, ribbon.h - 12);
|
|
2183
|
+
const purchase = {
|
|
2184
|
+
x: ribbon.x + ribbon.w - buttonWidth - 7,
|
|
2185
|
+
y: ribbon.y + (ribbon.h - buttonHeight) / 2,
|
|
2186
|
+
w: buttonWidth,
|
|
2187
|
+
h: buttonHeight,
|
|
2188
|
+
};
|
|
2189
|
+
ctx.fillStyle = "#75d8ef";
|
|
2190
|
+
this._licenseRect(ctx, purchase.x, purchase.y, purchase.w, purchase.h, 7);
|
|
2191
|
+
ctx.fill();
|
|
2192
|
+
ctx.textAlign = "center";
|
|
2193
|
+
ctx.textBaseline = "middle";
|
|
2194
|
+
ctx.fillStyle = "#061118";
|
|
2195
|
+
ctx.font = `750 ${clamp(buttonHeight * 0.32, 10, 12.5)}px Inter,system-ui,sans-serif`;
|
|
2196
|
+
ctx.fillText("Purchase license", purchase.x + purchase.w / 2, purchase.y + purchase.h / 2, purchase.w - 10);
|
|
2197
|
+
|
|
2198
|
+
ctx.textAlign = "left";
|
|
2199
|
+
ctx.fillStyle = "#d6dce6";
|
|
2200
|
+
ctx.font = `600 ${clamp(ribbonHeight * 0.24, 10, 13)}px Inter,system-ui,sans-serif`;
|
|
2201
|
+
const label = ribbon.w < 330 ? "License required" : "Unlicensed CanvasGlobe installation";
|
|
2202
|
+
ctx.fillText(label, ribbon.x + 14, ribbon.y + ribbon.h / 2, ribbon.w - buttonWidth - 35);
|
|
2203
|
+
ctx.restore();
|
|
2204
|
+
|
|
2205
|
+
this._licenseHits.push(
|
|
2206
|
+
{ action: "consume", ...ribbon },
|
|
2207
|
+
{ action: "purchase", ...purchase, url: notice.url },
|
|
2208
|
+
);
|
|
2209
|
+
this.canvas.setAttribute?.("data-canvas-globe-license-view", "watermark");
|
|
2057
2210
|
}
|
|
2058
2211
|
|
|
2059
2212
|
_paintCountries(t, trace, textured) {
|
package/src/index.js
CHANGED
package/src/license.js
CHANGED
|
@@ -4,6 +4,8 @@ export const DEFAULT_LICENSE_KEY = "0000-0000-000-0000";
|
|
|
4
4
|
export const LICENSE_KEY_PREFIX = "GLO";
|
|
5
5
|
export const LICENSE_PAGE_URL =
|
|
6
6
|
"https://canvasglobe.swiftools.com/pricing?utm_source=canvas-globe&utm_medium=runtime-notice";
|
|
7
|
+
export const LICENSE_SETUP_URL =
|
|
8
|
+
"https://canvasglobe.swiftools.com/license-key?utm_source=canvas-globe&utm_medium=runtime-notice";
|
|
7
9
|
|
|
8
10
|
// This remains false while the latest published line is GPLv3. The commercial
|
|
9
11
|
// release checklist requires an intentional switch after the agreement and
|
|
@@ -79,8 +81,9 @@ export function getLicensePresentation(
|
|
|
79
81
|
runtime,
|
|
80
82
|
notice: {
|
|
81
83
|
text: "CanvasGlobe: Purchase a license",
|
|
82
|
-
ariaLabel: "CanvasGlobe requires a license for production use.
|
|
84
|
+
ariaLabel: "CanvasGlobe requires a commercial license for production use.",
|
|
83
85
|
url: LICENSE_PAGE_URL,
|
|
86
|
+
setupUrl: LICENSE_SETUP_URL,
|
|
84
87
|
},
|
|
85
88
|
};
|
|
86
89
|
}
|
|
@@ -103,7 +106,7 @@ export function reportLicenseStatus(value, mode = COMMERCIAL_LICENSE_MODE) {
|
|
|
103
106
|
: `canvas-globe: ${DEFAULT_LICENSE_KEY} license key is not valid for production use. For help, email globe@swiftools.com`,
|
|
104
107
|
);
|
|
105
108
|
} else if (mode && status.kind === "invalid") {
|
|
106
|
-
console.error(`canvas-globe: enter the
|
|
109
|
+
console.error(`canvas-globe: enter the license key supplied after purchase. ${LICENSE_PAGE_URL}`);
|
|
107
110
|
}
|
|
108
111
|
return status;
|
|
109
112
|
}
|
package/src/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Keep in sync with package.json. Release checks enforce this value.
|
|
2
|
-
export const CANVAS_GLOBE_VERSION = "1.0.0-beta.
|
|
2
|
+
export const CANVAS_GLOBE_VERSION = "1.0.0-beta.3";
|
package/types/index.d.ts
CHANGED
|
@@ -610,6 +610,7 @@ export declare const DEFAULT_LICENSE_KEY: "0000-0000-000-0000";
|
|
|
610
610
|
export declare const LICENSE_KEY_PREFIX: "GLO";
|
|
611
611
|
/** Canonical licensing page used by CanvasGlobe notices. */
|
|
612
612
|
export declare const LICENSE_PAGE_URL: string;
|
|
613
|
+
export declare const LICENSE_SETUP_URL: string;
|
|
613
614
|
/** Classifies a browser location without making a network request. */
|
|
614
615
|
export declare function inspectRuntime(locationValue?: {
|
|
615
616
|
protocol?: string;
|