@freegridjs/core 1.9.0 → 1.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cdn/freegrid.esm.js +20 -20
- package/dist/cdn/freegrid.min.js +21 -21
- package/dist/index.d.ts +5 -4
- package/dist/index.js +80 -28
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export { default as en } from './locales/en.js';
|
|
|
5
5
|
declare function createGrid(host: HTMLElement, options: GridOptions): GridApi;
|
|
6
6
|
|
|
7
7
|
/** FreeGrid version (kept in sync with releases.json by scripts/release.mjs). */
|
|
8
|
-
declare const version = "1.9.
|
|
8
|
+
declare const version = "1.9.1";
|
|
9
9
|
|
|
10
10
|
interface LicenseInfo {
|
|
11
11
|
/** Licensed domains; each also covers every subdomain below it (`*.example.com` means the same as `example.com`). */
|
|
@@ -27,12 +27,13 @@ type LicenseResult = {
|
|
|
27
27
|
reason: 'format' | 'signature' | 'expired' | 'domain' | 'version' | 'unsupported';
|
|
28
28
|
info?: LicenseInfo;
|
|
29
29
|
};
|
|
30
|
-
|
|
31
|
-
declare function verifyLicense(key: string, host: string, opts?: {
|
|
30
|
+
type VerifyOptions = {
|
|
32
31
|
publicKey?: JsonWebKey;
|
|
33
32
|
now?: Date;
|
|
34
33
|
version?: string;
|
|
35
|
-
}
|
|
34
|
+
};
|
|
35
|
+
/** Check a key's signature, expiry, domain and (perpetual keys) the major version. */
|
|
36
|
+
declare function verifyLicense(key: string, host: string, opts?: VerifyOptions): Promise<LicenseResult>;
|
|
36
37
|
/** Set the license key once for every grid on the page. */
|
|
37
38
|
declare function setLicenseKey(key: string): void;
|
|
38
39
|
|
package/dist/index.js
CHANGED
|
@@ -3453,6 +3453,7 @@ function attachBody(grid) {
|
|
|
3453
3453
|
const keyOf = (e) => mirrored(grid, e);
|
|
3454
3454
|
let lastPointer = "mouse";
|
|
3455
3455
|
let pressTimer = 0;
|
|
3456
|
+
let endGesture = null;
|
|
3456
3457
|
let suppressClick = false;
|
|
3457
3458
|
const toEvent = (e) => {
|
|
3458
3459
|
const hit = renderer.cellAt(e.target);
|
|
@@ -3675,6 +3676,7 @@ function attachBody(grid) {
|
|
|
3675
3676
|
window.removeEventListener("pointermove", move);
|
|
3676
3677
|
window.removeEventListener("pointerup", cancel);
|
|
3677
3678
|
window.removeEventListener("pointercancel", cancel);
|
|
3679
|
+
if (endGesture === cancel) endGesture = null;
|
|
3678
3680
|
};
|
|
3679
3681
|
const move = (ev) => {
|
|
3680
3682
|
if (Math.hypot(ev.clientX - x0, ev.clientY - y0) > 8) cancel();
|
|
@@ -3690,6 +3692,8 @@ function attachBody(grid) {
|
|
|
3690
3692
|
}
|
|
3691
3693
|
grid.contextMenu.open(x0, y0, hit.rowIndex, hit.colId);
|
|
3692
3694
|
}, 550);
|
|
3695
|
+
endGesture?.();
|
|
3696
|
+
endGesture = cancel;
|
|
3693
3697
|
window.addEventListener("pointermove", move);
|
|
3694
3698
|
window.addEventListener("pointerup", cancel);
|
|
3695
3699
|
window.addEventListener("pointercancel", cancel);
|
|
@@ -3723,11 +3727,16 @@ function attachBody(grid) {
|
|
|
3723
3727
|
const up = () => {
|
|
3724
3728
|
window.removeEventListener("pointermove", move);
|
|
3725
3729
|
window.removeEventListener("pointerup", up);
|
|
3730
|
+
window.removeEventListener("pointercancel", up);
|
|
3726
3731
|
root.classList.remove("fg-range-dragging");
|
|
3727
3732
|
if (dragging) suppressClick = true;
|
|
3733
|
+
if (endGesture === up) endGesture = null;
|
|
3728
3734
|
};
|
|
3735
|
+
endGesture?.();
|
|
3736
|
+
endGesture = up;
|
|
3729
3737
|
window.addEventListener("pointermove", move);
|
|
3730
3738
|
window.addEventListener("pointerup", up);
|
|
3739
|
+
window.addEventListener("pointercancel", up);
|
|
3731
3740
|
};
|
|
3732
3741
|
const onKeyDown = (e) => {
|
|
3733
3742
|
if (e.target !== root && e.target !== renderer.gridEl) return;
|
|
@@ -3826,6 +3835,8 @@ function attachBody(grid) {
|
|
|
3826
3835
|
root.addEventListener("paste", onPaste);
|
|
3827
3836
|
root.addEventListener("cut", onCut);
|
|
3828
3837
|
return () => {
|
|
3838
|
+
clearTimeout(pressTimer);
|
|
3839
|
+
endGesture?.();
|
|
3829
3840
|
body.removeEventListener("click", onClick);
|
|
3830
3841
|
body.removeEventListener("dblclick", onDblClick);
|
|
3831
3842
|
body.removeEventListener("pointerdown", onPointerDown);
|
|
@@ -7633,7 +7644,7 @@ var PAID_FEATURES = {
|
|
|
7633
7644
|
};
|
|
7634
7645
|
|
|
7635
7646
|
// src/version.ts
|
|
7636
|
-
var version = "1.9.
|
|
7647
|
+
var version = "1.9.1";
|
|
7637
7648
|
|
|
7638
7649
|
// src/license.ts
|
|
7639
7650
|
var PUBLIC_KEY = { kty: "EC", crv: "P-256", x: "sUTKkglZmk-HMKPNub8p7AeLBKesHCU8irIkw9rJlbE", y: "j0U_aKRDR7iApY9UpaD3hUemhBLZJwDv9-QiT_hUyw8" };
|
|
@@ -7648,11 +7659,17 @@ var b64urlDecode = (s) => {
|
|
|
7648
7659
|
};
|
|
7649
7660
|
function isDevHost(host) {
|
|
7650
7661
|
const h = host.toLowerCase().replace(/^\[|\]$/g, "");
|
|
7651
|
-
|
|
7662
|
+
if (!h || h === "localhost" || h === "127.0.0.1" || h === "::1" || h === "0.0.0.0" || h.endsWith(".localhost") || h.endsWith(".test") || h.endsWith(".local")) return true;
|
|
7663
|
+
const ip = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(h);
|
|
7664
|
+
if (!ip) return false;
|
|
7665
|
+
const [a, b, c, d] = ip.slice(1).map(Number);
|
|
7666
|
+
if ([a, b, c, d].some((n) => n > 255)) return false;
|
|
7667
|
+
return a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31;
|
|
7652
7668
|
}
|
|
7653
7669
|
function domainMatches(host, domains) {
|
|
7654
7670
|
const h = host.toLowerCase().replace(/\.$/, "");
|
|
7655
7671
|
return domains.some((d) => {
|
|
7672
|
+
if (typeof d !== "string") return false;
|
|
7656
7673
|
const raw = d.toLowerCase().replace(/\.$/, "");
|
|
7657
7674
|
const dom = raw.startsWith("*.") ? raw.slice(2) : raw;
|
|
7658
7675
|
if (!dom) return false;
|
|
@@ -7666,20 +7683,32 @@ var isDay = (s) => {
|
|
|
7666
7683
|
return d.getUTCMonth() === Number(m[2]) - 1 && d.getUTCDate() === Number(m[3]);
|
|
7667
7684
|
};
|
|
7668
7685
|
var today = (now) => `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
7669
|
-
|
|
7686
|
+
function checkTerms(key, host, opts = {}) {
|
|
7670
7687
|
const parts = typeof key === "string" ? key.trim().split(".") : [];
|
|
7671
|
-
if (parts.length !== 3 || parts[0] !== "FG1") return { ok: false, reason: "format" };
|
|
7688
|
+
if (parts.length !== 3 || parts[0] !== "FG1") return { fail: { ok: false, reason: "format" } };
|
|
7672
7689
|
let info;
|
|
7673
7690
|
try {
|
|
7674
7691
|
info = JSON.parse(new TextDecoder().decode(b64urlDecode(parts[1])));
|
|
7675
|
-
if (!Array.isArray(info.domains) || !isDay(info.expires)) return { ok: false, reason: "format" };
|
|
7676
|
-
if (info.major !== void 0 && !(Number.isInteger(info.major) && info.major > 0)) return { ok: false, reason: "format" };
|
|
7677
|
-
if (info.plan !== void 0 && info.plan !== "company" && info.plan !== "personal") return { ok: false, reason: "format" };
|
|
7692
|
+
if (!info || !Array.isArray(info.domains) || !info.domains.every((d) => typeof d === "string") || !isDay(info.expires)) return { fail: { ok: false, reason: "format" } };
|
|
7693
|
+
if (info.major !== void 0 && !(Number.isInteger(info.major) && info.major > 0)) return { fail: { ok: false, reason: "format" } };
|
|
7694
|
+
if (info.plan !== void 0 && info.plan !== "company" && info.plan !== "personal") return { fail: { ok: false, reason: "format" } };
|
|
7678
7695
|
} catch {
|
|
7679
|
-
return { ok: false, reason: "format" };
|
|
7696
|
+
return { fail: { ok: false, reason: "format" } };
|
|
7680
7697
|
}
|
|
7681
|
-
|
|
7682
|
-
if (
|
|
7698
|
+
let fail = null;
|
|
7699
|
+
if (today(opts.now ?? /* @__PURE__ */ new Date()) > info.expires) fail = { ok: false, reason: "expired", info };
|
|
7700
|
+
else if (!domainMatches(host, info.domains)) fail = { ok: false, reason: "domain", info };
|
|
7701
|
+
else if (info.major !== void 0 && majorOf(opts.version ?? version) > info.major) fail = { ok: false, reason: "version", info };
|
|
7702
|
+
return { parts, info, fail };
|
|
7703
|
+
}
|
|
7704
|
+
var hasSubtle = () => typeof crypto !== "undefined" && !!crypto.subtle;
|
|
7705
|
+
async function verifyLicense(key, host, opts = {}) {
|
|
7706
|
+
const checked = checkTerms(key, host, opts);
|
|
7707
|
+
if (!("info" in checked)) return checked.fail;
|
|
7708
|
+
const { parts, info } = checked;
|
|
7709
|
+
const terms = () => checked.fail;
|
|
7710
|
+
if (!hasSubtle()) return terms() ?? { ok: false, reason: "unsupported", info };
|
|
7711
|
+
const subtle = crypto.subtle;
|
|
7683
7712
|
try {
|
|
7684
7713
|
const pub = await subtle.importKey("jwk", opts.publicKey ?? PUBLIC_KEY, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
|
|
7685
7714
|
const signed = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
|
|
@@ -7688,10 +7717,7 @@ async function verifyLicense(key, host, opts = {}) {
|
|
|
7688
7717
|
} catch {
|
|
7689
7718
|
return { ok: false, reason: "signature", info };
|
|
7690
7719
|
}
|
|
7691
|
-
|
|
7692
|
-
if (!domainMatches(host, info.domains)) return { ok: false, reason: "domain", info };
|
|
7693
|
-
if (info.major !== void 0 && majorOf(opts.version ?? version) > info.major) return { ok: false, reason: "version", info };
|
|
7694
|
-
return { ok: true, info };
|
|
7720
|
+
return terms() ?? { ok: true, info };
|
|
7695
7721
|
}
|
|
7696
7722
|
var globalKey;
|
|
7697
7723
|
var statuses = /* @__PURE__ */ new Map();
|
|
@@ -7729,23 +7755,26 @@ function reportUsage(info, host = typeof location !== "undefined" ? location.hos
|
|
|
7729
7755
|
}
|
|
7730
7756
|
function checkLicense(key = globalKey, host = typeof location !== "undefined" ? location.hostname : "") {
|
|
7731
7757
|
if (isDevHost(host)) return Promise.resolve(true);
|
|
7732
|
-
|
|
7758
|
+
return keyState(key ?? globalKey, host).then((s) => s === "ok" || s === "unsupported");
|
|
7759
|
+
}
|
|
7760
|
+
function keyState(k, host) {
|
|
7733
7761
|
const id = `${k ?? ""}@${host}`;
|
|
7734
7762
|
let status = statuses.get(id);
|
|
7735
7763
|
if (status) return status;
|
|
7736
7764
|
if (!k) {
|
|
7737
7765
|
console.info(NOTICE.missing);
|
|
7738
7766
|
reportUsage({ version, licensed: false }, host);
|
|
7739
|
-
status = Promise.resolve(
|
|
7767
|
+
status = Promise.resolve("none");
|
|
7740
7768
|
} else {
|
|
7741
7769
|
status = verifyLicense(k, host).then((res) => {
|
|
7742
7770
|
reportUsage({ version, licensed: res.ok, company: res.ok ? res.info.company : void 0 }, host);
|
|
7743
|
-
if (res.ok
|
|
7771
|
+
if (res.ok) return "ok";
|
|
7772
|
+
if (res.reason === "unsupported") return "unsupported";
|
|
7744
7773
|
const extra = res.info ? ` (${res.info.domains.join(", ")}, ${res.info.major ? `${res.info.major}.x` : res.info.expires})` : "";
|
|
7745
7774
|
console.warn(`${NOTICE[res.reason]}${extra}
|
|
7746
7775
|
${PRICING_URL}`);
|
|
7747
|
-
return
|
|
7748
|
-
}, () =>
|
|
7776
|
+
return "bad";
|
|
7777
|
+
}, () => "bad");
|
|
7749
7778
|
}
|
|
7750
7779
|
statuses.set(id, status);
|
|
7751
7780
|
return status;
|
|
@@ -7792,41 +7821,64 @@ function badgeRequested(host = currentHost()) {
|
|
|
7792
7821
|
return fetchDomainStatus(host).then((s) => s?.badge === true);
|
|
7793
7822
|
}
|
|
7794
7823
|
async function needsFreeBadge(key, host = currentHost()) {
|
|
7824
|
+
await Promise.resolve();
|
|
7795
7825
|
if (await checkLicense(key, host)) return false;
|
|
7796
7826
|
return badgeRequested(host);
|
|
7797
7827
|
}
|
|
7798
7828
|
var GATE_KEY = "freegrid:gate";
|
|
7829
|
+
var MAX_VERIFIED_KEYS = 5;
|
|
7799
7830
|
var keyIdOf = (key) => {
|
|
7800
7831
|
if (!key) return "";
|
|
7801
7832
|
const parts = key.trim().split(".");
|
|
7802
7833
|
return parts[parts.length - 1].slice(-16);
|
|
7803
7834
|
};
|
|
7835
|
+
var readGate = (host) => {
|
|
7836
|
+
const c = JSON.parse(localStorage.getItem(GATE_KEY) ?? "null");
|
|
7837
|
+
if (!c || typeof c !== "object" || c.host !== host || typeof c.trialEnded !== "boolean" || !Array.isArray(c.verifiedKeys) || !c.verifiedKeys.every((k) => typeof k === "string")) return null;
|
|
7838
|
+
return { host, trialEnded: c.trialEnded, ...typeof c.trialEnds === "string" ? { trialEnds: c.trialEnds } : {}, verifiedKeys: c.verifiedKeys };
|
|
7839
|
+
};
|
|
7804
7840
|
function paidAllowed(key, host = currentHost()) {
|
|
7805
7841
|
if (isDevHost(host)) return true;
|
|
7842
|
+
const k = key ?? globalKey;
|
|
7843
|
+
if (k && !hasSubtle()) {
|
|
7844
|
+
const checked = checkTerms(k, host);
|
|
7845
|
+
if ("info" in checked && !checked.fail) return true;
|
|
7846
|
+
}
|
|
7806
7847
|
try {
|
|
7807
|
-
const cache2 =
|
|
7808
|
-
if (!cache2
|
|
7809
|
-
|
|
7848
|
+
const cache2 = readGate(host);
|
|
7849
|
+
if (!cache2) return true;
|
|
7850
|
+
const id = keyIdOf(k);
|
|
7851
|
+
if (id && cache2.verifiedKeys.includes(id)) return true;
|
|
7852
|
+
return !cache2.trialEnded;
|
|
7810
7853
|
} catch {
|
|
7811
7854
|
return true;
|
|
7812
7855
|
}
|
|
7813
7856
|
}
|
|
7814
|
-
var
|
|
7857
|
+
var updateGate = (host, patch) => {
|
|
7815
7858
|
try {
|
|
7816
|
-
|
|
7859
|
+
let cur = null;
|
|
7860
|
+
try {
|
|
7861
|
+
cur = readGate(host);
|
|
7862
|
+
} catch {
|
|
7863
|
+
}
|
|
7864
|
+
localStorage.setItem(GATE_KEY, JSON.stringify(patch(cur ?? { host, trialEnded: false, verifiedKeys: [] })));
|
|
7817
7865
|
} catch {
|
|
7818
7866
|
}
|
|
7819
7867
|
};
|
|
7820
7868
|
async function resolveGate(key, host = currentHost()) {
|
|
7869
|
+
if (isDevHost(host)) return true;
|
|
7870
|
+
await Promise.resolve();
|
|
7821
7871
|
const k = key ?? globalKey;
|
|
7822
|
-
const
|
|
7823
|
-
if (
|
|
7824
|
-
|
|
7872
|
+
const state = await keyState(k, host);
|
|
7873
|
+
if (state === "ok") {
|
|
7874
|
+
const id = keyIdOf(k);
|
|
7875
|
+
updateGate(host, (c) => ({ ...c, verifiedKeys: [id, ...c.verifiedKeys.filter((x) => x !== id)].slice(0, MAX_VERIFIED_KEYS) }));
|
|
7825
7876
|
return true;
|
|
7826
7877
|
}
|
|
7878
|
+
if (state === "unsupported") return true;
|
|
7827
7879
|
const trial = (await fetchDomainStatus(host))?.trial;
|
|
7828
7880
|
if (!trial) return true;
|
|
7829
|
-
|
|
7881
|
+
updateGate(host, (c) => ({ ...c, trialEnded: !trial.active, trialEnds: trial.ends }));
|
|
7830
7882
|
const notice = `${trial.active ? "trial" : "ended"}@${host}`;
|
|
7831
7883
|
if (!gateNotices.has(notice)) {
|
|
7832
7884
|
gateNotices.add(notice);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@freegridjs/core",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "FreeGrid — a fast data grid for the web: virtual scrolling, grouping, pivot, charts, Excel export, 7 languages. Free core; paid features free with a personal key.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"type": "module",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
"unpkg": "./dist/cdn/freegrid.min.js",
|
|
49
49
|
"jsdelivr": "./dist/cdn/freegrid.min.js",
|
|
50
50
|
"scripts": {
|
|
51
|
-
"build": "tsup && node scripts/copy-css.mjs",
|
|
51
|
+
"build": "node scripts/clean-dist.mjs && tsup && node scripts/copy-css.mjs",
|
|
52
52
|
"test": "vitest run",
|
|
53
53
|
"typecheck": "tsc -p tsconfig.json"
|
|
54
54
|
}
|