@freegridjs/core 1.9.0 → 1.9.2
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.css +525 -525
- 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 +105 -28
- package/dist/styles.css +525 -525
- 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.2";
|
|
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
|
@@ -2271,6 +2271,24 @@ var Renderer = class {
|
|
|
2271
2271
|
else this.paintRow(row.el, node, i, cols, isSel, focusCol, hits, spans, range.first);
|
|
2272
2272
|
}
|
|
2273
2273
|
}
|
|
2274
|
+
if (spans.size) this.stickMergedText(top);
|
|
2275
|
+
}
|
|
2276
|
+
/**
|
|
2277
|
+
* A vertically merged cell taller than the view keeps its text in sight: the text moves down to the top of the
|
|
2278
|
+
* visible part of the block and stops in the block's last row. Runs on every scroll, also for rows not repainted.
|
|
2279
|
+
*/
|
|
2280
|
+
stickMergedText(viewTop) {
|
|
2281
|
+
for (const r of REGIONS3) {
|
|
2282
|
+
for (const row of this.painted[r].values()) {
|
|
2283
|
+
for (const c of row.el.querySelectorAll(".fg-cell-merged")) {
|
|
2284
|
+
const blockTop = Number(c.dataset.mergeTop);
|
|
2285
|
+
const last = Number(c.dataset.mergeLast);
|
|
2286
|
+
if (!Number.isFinite(blockTop) || !Number.isFinite(last)) continue;
|
|
2287
|
+
const shift = `${Math.round(Math.min(last, Math.max(0, viewTop - blockTop)))}px`;
|
|
2288
|
+
if (c.style.getPropertyValue("--fg-merge-shift") !== shift) c.style.setProperty("--fg-merge-shift", shift);
|
|
2289
|
+
}
|
|
2290
|
+
}
|
|
2291
|
+
}
|
|
2274
2292
|
}
|
|
2275
2293
|
/**
|
|
2276
2294
|
* Same row, only the column window moved: drop the cells that left and paint the ones that came in.
|
|
@@ -2349,9 +2367,16 @@ var Renderer = class {
|
|
|
2349
2367
|
cellEl.style.top = `${-(this.rowTop(i) - this.rowTop(s.start[i]))}px`;
|
|
2350
2368
|
cellEl.style.height = `${this.rowTop(s.end[i]) + this.rowH(s.end[i]) - this.rowTop(s.start[i])}px`;
|
|
2351
2369
|
cellEl.style.bottom = "auto";
|
|
2370
|
+
cellEl.dataset.mergeTop = String(this.rowTop(s.start[i]));
|
|
2371
|
+
cellEl.dataset.mergeLast = String(this.rowTop(s.end[i]) - this.rowTop(s.start[i]));
|
|
2352
2372
|
drawsMerge = true;
|
|
2353
2373
|
} else cellEl.classList.add("fg-cell-merge-hidden");
|
|
2354
2374
|
}
|
|
2375
|
+
if (!cellEl.classList.contains("fg-cell-merged") && cellEl.dataset.mergeTop !== void 0) {
|
|
2376
|
+
delete cellEl.dataset.mergeTop;
|
|
2377
|
+
delete cellEl.dataset.mergeLast;
|
|
2378
|
+
cellEl.style.removeProperty("--fg-merge-shift");
|
|
2379
|
+
}
|
|
2355
2380
|
if (!isSystemCol(col.id) && bounds.some((b) => col.index >= b.c1 && col.index <= b.c2)) cellEl.classList.add("fg-cell-range");
|
|
2356
2381
|
});
|
|
2357
2382
|
rowEl.style.zIndex = drawsMerge ? "1" : "";
|
|
@@ -3453,6 +3478,7 @@ function attachBody(grid) {
|
|
|
3453
3478
|
const keyOf = (e) => mirrored(grid, e);
|
|
3454
3479
|
let lastPointer = "mouse";
|
|
3455
3480
|
let pressTimer = 0;
|
|
3481
|
+
let endGesture = null;
|
|
3456
3482
|
let suppressClick = false;
|
|
3457
3483
|
const toEvent = (e) => {
|
|
3458
3484
|
const hit = renderer.cellAt(e.target);
|
|
@@ -3675,6 +3701,7 @@ function attachBody(grid) {
|
|
|
3675
3701
|
window.removeEventListener("pointermove", move);
|
|
3676
3702
|
window.removeEventListener("pointerup", cancel);
|
|
3677
3703
|
window.removeEventListener("pointercancel", cancel);
|
|
3704
|
+
if (endGesture === cancel) endGesture = null;
|
|
3678
3705
|
};
|
|
3679
3706
|
const move = (ev) => {
|
|
3680
3707
|
if (Math.hypot(ev.clientX - x0, ev.clientY - y0) > 8) cancel();
|
|
@@ -3690,6 +3717,8 @@ function attachBody(grid) {
|
|
|
3690
3717
|
}
|
|
3691
3718
|
grid.contextMenu.open(x0, y0, hit.rowIndex, hit.colId);
|
|
3692
3719
|
}, 550);
|
|
3720
|
+
endGesture?.();
|
|
3721
|
+
endGesture = cancel;
|
|
3693
3722
|
window.addEventListener("pointermove", move);
|
|
3694
3723
|
window.addEventListener("pointerup", cancel);
|
|
3695
3724
|
window.addEventListener("pointercancel", cancel);
|
|
@@ -3723,11 +3752,16 @@ function attachBody(grid) {
|
|
|
3723
3752
|
const up = () => {
|
|
3724
3753
|
window.removeEventListener("pointermove", move);
|
|
3725
3754
|
window.removeEventListener("pointerup", up);
|
|
3755
|
+
window.removeEventListener("pointercancel", up);
|
|
3726
3756
|
root.classList.remove("fg-range-dragging");
|
|
3727
3757
|
if (dragging) suppressClick = true;
|
|
3758
|
+
if (endGesture === up) endGesture = null;
|
|
3728
3759
|
};
|
|
3760
|
+
endGesture?.();
|
|
3761
|
+
endGesture = up;
|
|
3729
3762
|
window.addEventListener("pointermove", move);
|
|
3730
3763
|
window.addEventListener("pointerup", up);
|
|
3764
|
+
window.addEventListener("pointercancel", up);
|
|
3731
3765
|
};
|
|
3732
3766
|
const onKeyDown = (e) => {
|
|
3733
3767
|
if (e.target !== root && e.target !== renderer.gridEl) return;
|
|
@@ -3826,6 +3860,8 @@ function attachBody(grid) {
|
|
|
3826
3860
|
root.addEventListener("paste", onPaste);
|
|
3827
3861
|
root.addEventListener("cut", onCut);
|
|
3828
3862
|
return () => {
|
|
3863
|
+
clearTimeout(pressTimer);
|
|
3864
|
+
endGesture?.();
|
|
3829
3865
|
body.removeEventListener("click", onClick);
|
|
3830
3866
|
body.removeEventListener("dblclick", onDblClick);
|
|
3831
3867
|
body.removeEventListener("pointerdown", onPointerDown);
|
|
@@ -7633,7 +7669,7 @@ var PAID_FEATURES = {
|
|
|
7633
7669
|
};
|
|
7634
7670
|
|
|
7635
7671
|
// src/version.ts
|
|
7636
|
-
var version = "1.9.
|
|
7672
|
+
var version = "1.9.2";
|
|
7637
7673
|
|
|
7638
7674
|
// src/license.ts
|
|
7639
7675
|
var PUBLIC_KEY = { kty: "EC", crv: "P-256", x: "sUTKkglZmk-HMKPNub8p7AeLBKesHCU8irIkw9rJlbE", y: "j0U_aKRDR7iApY9UpaD3hUemhBLZJwDv9-QiT_hUyw8" };
|
|
@@ -7648,11 +7684,17 @@ var b64urlDecode = (s) => {
|
|
|
7648
7684
|
};
|
|
7649
7685
|
function isDevHost(host) {
|
|
7650
7686
|
const h = host.toLowerCase().replace(/^\[|\]$/g, "");
|
|
7651
|
-
|
|
7687
|
+
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;
|
|
7688
|
+
const ip = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(h);
|
|
7689
|
+
if (!ip) return false;
|
|
7690
|
+
const [a, b, c, d] = ip.slice(1).map(Number);
|
|
7691
|
+
if ([a, b, c, d].some((n) => n > 255)) return false;
|
|
7692
|
+
return a === 10 || a === 192 && b === 168 || a === 172 && b >= 16 && b <= 31;
|
|
7652
7693
|
}
|
|
7653
7694
|
function domainMatches(host, domains) {
|
|
7654
7695
|
const h = host.toLowerCase().replace(/\.$/, "");
|
|
7655
7696
|
return domains.some((d) => {
|
|
7697
|
+
if (typeof d !== "string") return false;
|
|
7656
7698
|
const raw = d.toLowerCase().replace(/\.$/, "");
|
|
7657
7699
|
const dom = raw.startsWith("*.") ? raw.slice(2) : raw;
|
|
7658
7700
|
if (!dom) return false;
|
|
@@ -7666,20 +7708,32 @@ var isDay = (s) => {
|
|
|
7666
7708
|
return d.getUTCMonth() === Number(m[2]) - 1 && d.getUTCDate() === Number(m[3]);
|
|
7667
7709
|
};
|
|
7668
7710
|
var today = (now) => `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}-${String(now.getDate()).padStart(2, "0")}`;
|
|
7669
|
-
|
|
7711
|
+
function checkTerms(key, host, opts = {}) {
|
|
7670
7712
|
const parts = typeof key === "string" ? key.trim().split(".") : [];
|
|
7671
|
-
if (parts.length !== 3 || parts[0] !== "FG1") return { ok: false, reason: "format" };
|
|
7713
|
+
if (parts.length !== 3 || parts[0] !== "FG1") return { fail: { ok: false, reason: "format" } };
|
|
7672
7714
|
let info;
|
|
7673
7715
|
try {
|
|
7674
7716
|
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" };
|
|
7717
|
+
if (!info || !Array.isArray(info.domains) || !info.domains.every((d) => typeof d === "string") || !isDay(info.expires)) return { fail: { ok: false, reason: "format" } };
|
|
7718
|
+
if (info.major !== void 0 && !(Number.isInteger(info.major) && info.major > 0)) return { fail: { ok: false, reason: "format" } };
|
|
7719
|
+
if (info.plan !== void 0 && info.plan !== "company" && info.plan !== "personal") return { fail: { ok: false, reason: "format" } };
|
|
7678
7720
|
} catch {
|
|
7679
|
-
return { ok: false, reason: "format" };
|
|
7721
|
+
return { fail: { ok: false, reason: "format" } };
|
|
7680
7722
|
}
|
|
7681
|
-
|
|
7682
|
-
if (
|
|
7723
|
+
let fail = null;
|
|
7724
|
+
if (today(opts.now ?? /* @__PURE__ */ new Date()) > info.expires) fail = { ok: false, reason: "expired", info };
|
|
7725
|
+
else if (!domainMatches(host, info.domains)) fail = { ok: false, reason: "domain", info };
|
|
7726
|
+
else if (info.major !== void 0 && majorOf(opts.version ?? version) > info.major) fail = { ok: false, reason: "version", info };
|
|
7727
|
+
return { parts, info, fail };
|
|
7728
|
+
}
|
|
7729
|
+
var hasSubtle = () => typeof crypto !== "undefined" && !!crypto.subtle;
|
|
7730
|
+
async function verifyLicense(key, host, opts = {}) {
|
|
7731
|
+
const checked = checkTerms(key, host, opts);
|
|
7732
|
+
if (!("info" in checked)) return checked.fail;
|
|
7733
|
+
const { parts, info } = checked;
|
|
7734
|
+
const terms = () => checked.fail;
|
|
7735
|
+
if (!hasSubtle()) return terms() ?? { ok: false, reason: "unsupported", info };
|
|
7736
|
+
const subtle = crypto.subtle;
|
|
7683
7737
|
try {
|
|
7684
7738
|
const pub = await subtle.importKey("jwk", opts.publicKey ?? PUBLIC_KEY, { name: "ECDSA", namedCurve: "P-256" }, false, ["verify"]);
|
|
7685
7739
|
const signed = new TextEncoder().encode(`${parts[0]}.${parts[1]}`);
|
|
@@ -7688,10 +7742,7 @@ async function verifyLicense(key, host, opts = {}) {
|
|
|
7688
7742
|
} catch {
|
|
7689
7743
|
return { ok: false, reason: "signature", info };
|
|
7690
7744
|
}
|
|
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 };
|
|
7745
|
+
return terms() ?? { ok: true, info };
|
|
7695
7746
|
}
|
|
7696
7747
|
var globalKey;
|
|
7697
7748
|
var statuses = /* @__PURE__ */ new Map();
|
|
@@ -7729,23 +7780,26 @@ function reportUsage(info, host = typeof location !== "undefined" ? location.hos
|
|
|
7729
7780
|
}
|
|
7730
7781
|
function checkLicense(key = globalKey, host = typeof location !== "undefined" ? location.hostname : "") {
|
|
7731
7782
|
if (isDevHost(host)) return Promise.resolve(true);
|
|
7732
|
-
|
|
7783
|
+
return keyState(key ?? globalKey, host).then((s) => s === "ok" || s === "unsupported");
|
|
7784
|
+
}
|
|
7785
|
+
function keyState(k, host) {
|
|
7733
7786
|
const id = `${k ?? ""}@${host}`;
|
|
7734
7787
|
let status = statuses.get(id);
|
|
7735
7788
|
if (status) return status;
|
|
7736
7789
|
if (!k) {
|
|
7737
7790
|
console.info(NOTICE.missing);
|
|
7738
7791
|
reportUsage({ version, licensed: false }, host);
|
|
7739
|
-
status = Promise.resolve(
|
|
7792
|
+
status = Promise.resolve("none");
|
|
7740
7793
|
} else {
|
|
7741
7794
|
status = verifyLicense(k, host).then((res) => {
|
|
7742
7795
|
reportUsage({ version, licensed: res.ok, company: res.ok ? res.info.company : void 0 }, host);
|
|
7743
|
-
if (res.ok
|
|
7796
|
+
if (res.ok) return "ok";
|
|
7797
|
+
if (res.reason === "unsupported") return "unsupported";
|
|
7744
7798
|
const extra = res.info ? ` (${res.info.domains.join(", ")}, ${res.info.major ? `${res.info.major}.x` : res.info.expires})` : "";
|
|
7745
7799
|
console.warn(`${NOTICE[res.reason]}${extra}
|
|
7746
7800
|
${PRICING_URL}`);
|
|
7747
|
-
return
|
|
7748
|
-
}, () =>
|
|
7801
|
+
return "bad";
|
|
7802
|
+
}, () => "bad");
|
|
7749
7803
|
}
|
|
7750
7804
|
statuses.set(id, status);
|
|
7751
7805
|
return status;
|
|
@@ -7792,41 +7846,64 @@ function badgeRequested(host = currentHost()) {
|
|
|
7792
7846
|
return fetchDomainStatus(host).then((s) => s?.badge === true);
|
|
7793
7847
|
}
|
|
7794
7848
|
async function needsFreeBadge(key, host = currentHost()) {
|
|
7849
|
+
await Promise.resolve();
|
|
7795
7850
|
if (await checkLicense(key, host)) return false;
|
|
7796
7851
|
return badgeRequested(host);
|
|
7797
7852
|
}
|
|
7798
7853
|
var GATE_KEY = "freegrid:gate";
|
|
7854
|
+
var MAX_VERIFIED_KEYS = 5;
|
|
7799
7855
|
var keyIdOf = (key) => {
|
|
7800
7856
|
if (!key) return "";
|
|
7801
7857
|
const parts = key.trim().split(".");
|
|
7802
7858
|
return parts[parts.length - 1].slice(-16);
|
|
7803
7859
|
};
|
|
7860
|
+
var readGate = (host) => {
|
|
7861
|
+
const c = JSON.parse(localStorage.getItem(GATE_KEY) ?? "null");
|
|
7862
|
+
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;
|
|
7863
|
+
return { host, trialEnded: c.trialEnded, ...typeof c.trialEnds === "string" ? { trialEnds: c.trialEnds } : {}, verifiedKeys: c.verifiedKeys };
|
|
7864
|
+
};
|
|
7804
7865
|
function paidAllowed(key, host = currentHost()) {
|
|
7805
7866
|
if (isDevHost(host)) return true;
|
|
7867
|
+
const k = key ?? globalKey;
|
|
7868
|
+
if (k && !hasSubtle()) {
|
|
7869
|
+
const checked = checkTerms(k, host);
|
|
7870
|
+
if ("info" in checked && !checked.fail) return true;
|
|
7871
|
+
}
|
|
7806
7872
|
try {
|
|
7807
|
-
const cache2 =
|
|
7808
|
-
if (!cache2
|
|
7809
|
-
|
|
7873
|
+
const cache2 = readGate(host);
|
|
7874
|
+
if (!cache2) return true;
|
|
7875
|
+
const id = keyIdOf(k);
|
|
7876
|
+
if (id && cache2.verifiedKeys.includes(id)) return true;
|
|
7877
|
+
return !cache2.trialEnded;
|
|
7810
7878
|
} catch {
|
|
7811
7879
|
return true;
|
|
7812
7880
|
}
|
|
7813
7881
|
}
|
|
7814
|
-
var
|
|
7882
|
+
var updateGate = (host, patch) => {
|
|
7815
7883
|
try {
|
|
7816
|
-
|
|
7884
|
+
let cur = null;
|
|
7885
|
+
try {
|
|
7886
|
+
cur = readGate(host);
|
|
7887
|
+
} catch {
|
|
7888
|
+
}
|
|
7889
|
+
localStorage.setItem(GATE_KEY, JSON.stringify(patch(cur ?? { host, trialEnded: false, verifiedKeys: [] })));
|
|
7817
7890
|
} catch {
|
|
7818
7891
|
}
|
|
7819
7892
|
};
|
|
7820
7893
|
async function resolveGate(key, host = currentHost()) {
|
|
7894
|
+
if (isDevHost(host)) return true;
|
|
7895
|
+
await Promise.resolve();
|
|
7821
7896
|
const k = key ?? globalKey;
|
|
7822
|
-
const
|
|
7823
|
-
if (
|
|
7824
|
-
|
|
7897
|
+
const state = await keyState(k, host);
|
|
7898
|
+
if (state === "ok") {
|
|
7899
|
+
const id = keyIdOf(k);
|
|
7900
|
+
updateGate(host, (c) => ({ ...c, verifiedKeys: [id, ...c.verifiedKeys.filter((x) => x !== id)].slice(0, MAX_VERIFIED_KEYS) }));
|
|
7825
7901
|
return true;
|
|
7826
7902
|
}
|
|
7903
|
+
if (state === "unsupported") return true;
|
|
7827
7904
|
const trial = (await fetchDomainStatus(host))?.trial;
|
|
7828
7905
|
if (!trial) return true;
|
|
7829
|
-
|
|
7906
|
+
updateGate(host, (c) => ({ ...c, trialEnded: !trial.active, trialEnds: trial.ends }));
|
|
7830
7907
|
const notice = `${trial.active ? "trial" : "ended"}@${host}`;
|
|
7831
7908
|
if (!gateNotices.has(notice)) {
|
|
7832
7909
|
gateNotices.add(notice);
|